Implement base function calls.
As an example two functions are added: - Create notification. - Clear notifications.
This commit is contained in:
parent
3f24489138
commit
41da29f669
@ -133,7 +133,12 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Tuple2<String, Consumer<String>> result) {
|
protected void onPostExecute(Tuple2<String, Consumer<String>> result) {
|
||||||
result.item2.apply(result.item1);
|
try {
|
||||||
|
result.item2.apply(result.item1);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
Log.e(LogTag, "Error on login UI update", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,10 @@ import android.content.Context;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.ConfigManager;
|
import com.codigoparallevar.minicards.ConfigManager;
|
||||||
|
import com.codigoparallevar.minicards.bridge.blocks.DefaultAndroidBlocks;
|
||||||
import com.programaker.bridge.ProgramakerBridge;
|
import com.programaker.bridge.ProgramakerBridge;
|
||||||
import com.programaker.bridge.ProgramakerBridgeConfiguration;
|
import com.programaker.bridge.ProgramakerBridgeConfiguration;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class ProgramakerAndroidBridge {
|
public class ProgramakerAndroidBridge {
|
||||||
private static final String LogTag = "PM Android Bridge";
|
private static final String LogTag = "PM Android Bridge";
|
||||||
private ProgramakerBridge bridgeRunner = null;
|
private ProgramakerBridge bridgeRunner = null;
|
||||||
@ -24,12 +23,7 @@ public class ProgramakerAndroidBridge {
|
|||||||
return serviceName;
|
return serviceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static ProgramakerBridgeConfiguration GetConfiguration(Context ctx) {
|
|
||||||
// List<ProgramakerBridgeConfigurationBlock> blocks = new LinkedList<>();
|
|
||||||
// return new ProgramakerBridgeConfiguration(serviceName, blocks);
|
|
||||||
// }
|
|
||||||
// Builder
|
// Builder
|
||||||
|
|
||||||
private final Context ctx;
|
private final Context ctx;
|
||||||
private final String userId;
|
private final String userId;
|
||||||
private final String bridgeId;
|
private final String bridgeId;
|
||||||
@ -44,7 +38,7 @@ public class ProgramakerAndroidBridge {
|
|||||||
ProgramakerBridgeConfiguration configuration = new ProgramakerBridgeConfiguration(
|
ProgramakerBridgeConfiguration configuration = new ProgramakerBridgeConfiguration(
|
||||||
ProgramakerAndroidBridge.GetBridgeName(this.ctx),
|
ProgramakerAndroidBridge.GetBridgeName(this.ctx),
|
||||||
new ConfigManager(this.ctx),
|
new ConfigManager(this.ctx),
|
||||||
Collections.emptyList()
|
DefaultAndroidBlocks.GetBuilder(this.ctx).Build()
|
||||||
);
|
);
|
||||||
|
|
||||||
this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, configuration, onReady, onComplete);
|
this.bridgeRunner = new ProgramakerBridge(this.bridgeId, this.userId, configuration, onReady, onComplete);
|
||||||
|
@ -14,6 +14,8 @@ import com.codigoparallevar.minicards.ui_helpers.DoAsync;
|
|||||||
import com.programaker.api.ProgramakerApi;
|
import com.programaker.api.ProgramakerApi;
|
||||||
|
|
||||||
public class ProgramakerBridgeService extends Service {
|
public class ProgramakerBridgeService extends Service {
|
||||||
|
public static final String BridgeUserNotificationChannel = "PROGRAMAKER_BRIDGE_USER_NOTIFICATION";
|
||||||
|
public static final CharSequence BridgeUserNotificationChannelName = "User notifications";
|
||||||
private ProgramakerAndroidBridge bridge = null;
|
private ProgramakerAndroidBridge bridge = null;
|
||||||
private static final String LogTag = "PM BridgeService";
|
private static final String LogTag = "PM BridgeService";
|
||||||
|
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
class BlockArgumentDefinition {
|
||||||
|
private final static String LogTag = "PM BlockArgument";
|
||||||
|
private final Type type;
|
||||||
|
private final String defaultValue;
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
STRING,
|
||||||
|
INT,
|
||||||
|
FLOAT,
|
||||||
|
BOOL,
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockArgumentDefinition(BlockArgumentDefinition.Type type, String defaultValue) {
|
||||||
|
this.type = type;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JSONObject serialize() throws JSONException {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
|
||||||
|
obj.put("type", BlockArgumentDefinition.TypeToString(type));
|
||||||
|
obj.put("default", this.defaultValue);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String TypeToString(Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case STRING:
|
||||||
|
return "string";
|
||||||
|
case INT:
|
||||||
|
return "integer";
|
||||||
|
case FLOAT:
|
||||||
|
return "float";
|
||||||
|
case BOOL:
|
||||||
|
return "bool";
|
||||||
|
default:
|
||||||
|
Log.e(LogTag, "Unknown type: " + type);
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||||
|
import com.programaker.bridge.ProgramakerBridgeConfigurationBlock;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class BridgeBlockListBuilder {
|
||||||
|
private final LinkedList<ProgramakerBridgeConfigurationBlock> blockList;
|
||||||
|
|
||||||
|
public BridgeBlockListBuilder() {
|
||||||
|
this.blockList = new LinkedList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<ProgramakerBridgeConfigurationBlock> Build() {
|
||||||
|
return blockList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BridgeBlockListBuilder addOperation(String id, String message,
|
||||||
|
List<BlockArgumentDefinition> arguments,
|
||||||
|
Consumer<List<? extends Object>> operation) {
|
||||||
|
return this.addOperation(new OperationBlockDefinition(id, message, arguments, operation));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BridgeBlockListBuilder addOperation(ProgramakerBridgeConfigurationBlock block) {
|
||||||
|
this.blockList.add(block);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.R;
|
||||||
|
import com.codigoparallevar.minicards.bridge.ProgramakerBridgeService;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class DefaultAndroidBlocks {
|
||||||
|
|
||||||
|
public static BridgeBlockListBuilder GetBuilder(Context ctx) {
|
||||||
|
// System services
|
||||||
|
String notifServiceId = Context.NOTIFICATION_SERVICE;
|
||||||
|
NotificationManager notificationManager = (NotificationManager) ctx.getSystemService(notifServiceId);
|
||||||
|
assert notificationManager != null;
|
||||||
|
|
||||||
|
Random notificationRandom = new Random();
|
||||||
|
|
||||||
|
String notificationChannelId = ProgramakerBridgeService.BridgeUserNotificationChannel;
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||||
|
NotificationChannel channel = new NotificationChannel(
|
||||||
|
ProgramakerBridgeService.BridgeUserNotificationChannel,
|
||||||
|
ProgramakerBridgeService.BridgeUserNotificationChannelName,
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
|
notificationManager.createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return new BridgeBlockListBuilder()
|
||||||
|
// Notifications
|
||||||
|
.addOperation(
|
||||||
|
"notifications_new",
|
||||||
|
"Create notification. Title: %1, text: %2",
|
||||||
|
new LinkedList<BlockArgumentDefinition>() {{
|
||||||
|
add(new BlockArgumentDefinition(BlockArgumentDefinition.Type.STRING, "My notification"));
|
||||||
|
add(new BlockArgumentDefinition(BlockArgumentDefinition.Type.STRING, "Sample description"));
|
||||||
|
}},
|
||||||
|
(List<?> params) -> {
|
||||||
|
if (params.size() != 2) {
|
||||||
|
throw new Exception("Expected two (2) arguments, found " + params.size());
|
||||||
|
}
|
||||||
|
String title = params.get(0).toString();
|
||||||
|
String description = params.get(1).toString();
|
||||||
|
|
||||||
|
Notification notif = new NotificationCompat
|
||||||
|
.Builder(ctx, ProgramakerBridgeService.BridgeUserNotificationChannel)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setContentText(description)
|
||||||
|
.setSmallIcon(R.drawable.ic_center_focus_weak_black) // TODO: Change icon
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
int notificationId = notificationRandom.nextInt();
|
||||||
|
notificationManager.notify(notificationId, notif);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.addOperation(
|
||||||
|
"notifications_clear",
|
||||||
|
"Clear notifications",
|
||||||
|
Collections.emptyList(),
|
||||||
|
(List<?> params) -> {
|
||||||
|
notificationManager.cancelAll();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.codigoparallevar.minicards.bridge.blocks;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||||
|
import com.programaker.bridge.ProgramakerBridgeConfigurationBlock;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class OperationBlockDefinition implements ProgramakerBridgeConfigurationBlock {
|
||||||
|
private final static String LogTag = "PM OpBlockDefinition";
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String message;
|
||||||
|
private final List<BlockArgumentDefinition> args;
|
||||||
|
private final Consumer<List<?>> operation;
|
||||||
|
|
||||||
|
public OperationBlockDefinition(String id, String message, List<BlockArgumentDefinition> args, Consumer<List<? extends Object>> operation) {
|
||||||
|
this.id = id;
|
||||||
|
this.message = message;
|
||||||
|
this.args = args;
|
||||||
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public JSONObject serialize() {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONArray arguments = new JSONArray();
|
||||||
|
for (BlockArgumentDefinition arg : this.args) {
|
||||||
|
arguments.put(arg.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.put("id", this.id);
|
||||||
|
obj.put("function_name", this.id);
|
||||||
|
obj.put("message", this.message);
|
||||||
|
obj.put("block_type", "operation");
|
||||||
|
obj.put("block_result_type", JSONObject.NULL);
|
||||||
|
obj.put("arguments", arguments);
|
||||||
|
obj.put("save_to", JSONObject.NULL);
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
Log.e(LogTag, "Error serializing block definition: " + ex, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getFunctionName() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void call(@NotNull List<?> arguments) throws Exception {
|
||||||
|
this.operation.apply(arguments);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
package com.codigoparallevar.minicards.types.functional;
|
package com.codigoparallevar.minicards.types.functional;
|
||||||
|
|
||||||
public interface Consumer<T> {
|
public interface Consumer<T> {
|
||||||
void apply(T param);
|
void apply(T param) throws Exception;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.codigoparallevar.minicards.ui_helpers;
|
package com.codigoparallevar.minicards.ui_helpers;
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.codigoparallevar.minicards.types.functional.Action;
|
import com.codigoparallevar.minicards.types.functional.Action;
|
||||||
import com.codigoparallevar.minicards.types.functional.Consumer;
|
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||||
@ -15,7 +16,11 @@ public class DoAsync extends AsyncTask<Tuple2<Action, Consumer<Throwable>>,
|
|||||||
data[0].item1.run();
|
data[0].item1.run();
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
data[0].item2.apply(ex);
|
try {
|
||||||
|
data[0].item2.apply(ex);
|
||||||
|
} catch (Throwable subEx) {
|
||||||
|
Log.e("DoAsync", "Error handling exception", subEx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,12 @@ public class GetAsync<T> extends AsyncTask<Tuple3<Producer<T>, Consumer<T>, Cons
|
|||||||
return new Tuple2<>(result, data[0]._y);
|
return new Tuple2<>(result, data[0]._y);
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
data[0]._z.apply(ex);
|
try {
|
||||||
|
data[0]._z.apply(ex);
|
||||||
|
}
|
||||||
|
catch (Throwable subEx) {
|
||||||
|
Log.d("GetAsync", "Error handling exception", subEx);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,7 +35,12 @@ public class GetAsync<T> extends AsyncTask<Tuple3<Producer<T>, Consumer<T>, Cons
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result.item2.apply(result.item1);
|
try {
|
||||||
|
result.item2.apply(result.item1);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
Log.e("GetAsync", "Error on UI thread", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,7 @@ class ProgramakerBridge(
|
|||||||
when (type) {
|
when (type) {
|
||||||
"GET_HOW_TO_SERVICE_REGISTRATION" -> handleGetServiceRegistrationInfo(webSocket, value, messageId, userId, extraData)
|
"GET_HOW_TO_SERVICE_REGISTRATION" -> handleGetServiceRegistrationInfo(webSocket, value, messageId, userId, extraData)
|
||||||
"REGISTRATION" -> handlePerformRegistration(webSocket, value, messageId, userId, extraData)
|
"REGISTRATION" -> handlePerformRegistration(webSocket, value, messageId, userId, extraData)
|
||||||
|
"FUNCTION_CALL" -> handleFunctionCall(webSocket, value, messageId, userId, extraData)
|
||||||
else ->
|
else ->
|
||||||
{
|
{
|
||||||
Log.w(LogTag, "Unknown command type: $type")
|
Log.w(LogTag, "Unknown command type: $type")
|
||||||
@ -104,6 +105,38 @@ class ProgramakerBridge(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleFunctionCall(webSocket: WebSocket, value: Map<*, *>, messageId: String, userId: String?, extraData: Map<*, *>?) {
|
||||||
|
val functionName = value.get("function_name") as String
|
||||||
|
val arguments = value.get("arguments") as List<*>
|
||||||
|
|
||||||
|
try {
|
||||||
|
config.callFunction(functionName, arguments)
|
||||||
|
|
||||||
|
val result = JSONObject.NULL
|
||||||
|
webSocket.send(
|
||||||
|
JSONObject(
|
||||||
|
hashMapOf(
|
||||||
|
"message_id" to messageId,
|
||||||
|
"success" to true,
|
||||||
|
"result" to result
|
||||||
|
) as Map<*, *>
|
||||||
|
).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
catch (ex: Throwable) {
|
||||||
|
Log.w(LogTag, "Error on bridge call to $functionName", ex)
|
||||||
|
webSocket.send(
|
||||||
|
JSONObject(
|
||||||
|
hashMapOf(
|
||||||
|
"message_id" to messageId,
|
||||||
|
"success" to false
|
||||||
|
) as Map<*, *>
|
||||||
|
).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleGetServiceRegistrationInfo(webSocket: WebSocket, value: Map<*, *>, messageId: String?, userId: String?, extraData: Map<*, *>?) {
|
private fun handleGetServiceRegistrationInfo(webSocket: WebSocket, value: Map<*, *>, messageId: String?, userId: String?, extraData: Map<*, *>?) {
|
||||||
// Registration is automatic
|
// Registration is automatic
|
||||||
webSocket.send(
|
webSocket.send(
|
||||||
|
@ -12,6 +12,7 @@ class ProgramakerBridgeConfiguration(
|
|||||||
// val allow_multiple_connections: boolean // No reason for this use case
|
// val allow_multiple_connections: boolean // No reason for this use case
|
||||||
// val icon: ???, // Not supported yet // TODO: Add support
|
// val icon: ???, // Not supported yet // TODO: Add support
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun serialize(): String {
|
fun serialize(): String {
|
||||||
var serializedBlocks = listOf<JSONObject>()
|
var serializedBlocks = listOf<JSONObject>()
|
||||||
if (blocks != null) {
|
if (blocks != null) {
|
||||||
@ -34,4 +35,14 @@ class ProgramakerBridgeConfiguration(
|
|||||||
|
|
||||||
return wrapper.toString()
|
return wrapper.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun callFunction(functionName: String, arguments: List<*>) {
|
||||||
|
for (block in blocks) {
|
||||||
|
if (block.getFunctionName() == functionName) {
|
||||||
|
return block.call(arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw IllegalArgumentException("Bridge function (name=$functionName) not found")
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,10 +2,10 @@ package com.programaker.bridge
|
|||||||
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class ProgramakerBridgeConfigurationBlock {
|
interface ProgramakerBridgeConfigurationBlock {
|
||||||
fun serialize() : JSONObject {
|
fun serialize() : JSONObject;
|
||||||
val obj = JSONObject();
|
fun getFunctionName(): String;
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun call(arguments: List<*>)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user