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,8 +133,13 @@ public class DeckPreviewActivity extends ReloadableAppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Tuple2<String, Consumer<String>> result) {
|
||||
try {
|
||||
result.item2.apply(result.item1);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
Log.e(LogTag, "Error on login UI update", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,11 +4,10 @@ import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.codigoparallevar.minicards.ConfigManager;
|
||||
import com.codigoparallevar.minicards.bridge.blocks.DefaultAndroidBlocks;
|
||||
import com.programaker.bridge.ProgramakerBridge;
|
||||
import com.programaker.bridge.ProgramakerBridgeConfiguration;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class ProgramakerAndroidBridge {
|
||||
private static final String LogTag = "PM Android Bridge";
|
||||
private ProgramakerBridge bridgeRunner = null;
|
||||
@ -24,12 +23,7 @@ public class ProgramakerAndroidBridge {
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
// public static ProgramakerBridgeConfiguration GetConfiguration(Context ctx) {
|
||||
// List<ProgramakerBridgeConfigurationBlock> blocks = new LinkedList<>();
|
||||
// return new ProgramakerBridgeConfiguration(serviceName, blocks);
|
||||
// }
|
||||
// Builder
|
||||
|
||||
private final Context ctx;
|
||||
private final String userId;
|
||||
private final String bridgeId;
|
||||
@ -44,7 +38,7 @@ public class ProgramakerAndroidBridge {
|
||||
ProgramakerBridgeConfiguration configuration = new ProgramakerBridgeConfiguration(
|
||||
ProgramakerAndroidBridge.GetBridgeName(this.ctx),
|
||||
new ConfigManager(this.ctx),
|
||||
Collections.emptyList()
|
||||
DefaultAndroidBlocks.GetBuilder(this.ctx).Build()
|
||||
);
|
||||
|
||||
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;
|
||||
|
||||
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 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;
|
||||
|
||||
public interface Consumer<T> {
|
||||
void apply(T param);
|
||||
void apply(T param) throws Exception;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.codigoparallevar.minicards.ui_helpers;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import com.codigoparallevar.minicards.types.functional.Action;
|
||||
import com.codigoparallevar.minicards.types.functional.Consumer;
|
||||
@ -15,7 +16,11 @@ public class DoAsync extends AsyncTask<Tuple2<Action, Consumer<Throwable>>,
|
||||
data[0].item1.run();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
try {
|
||||
data[0].item2.apply(ex);
|
||||
} catch (Throwable subEx) {
|
||||
Log.e("DoAsync", "Error handling exception", subEx);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
try {
|
||||
data[0]._z.apply(ex);
|
||||
}
|
||||
catch (Throwable subEx) {
|
||||
Log.d("GetAsync", "Error handling exception", subEx);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -30,7 +35,12 @@ public class GetAsync<T> extends AsyncTask<Tuple3<Producer<T>, Consumer<T>, Cons
|
||||
return;
|
||||
}
|
||||
else {
|
||||
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) {
|
||||
"GET_HOW_TO_SERVICE_REGISTRATION" -> handleGetServiceRegistrationInfo(webSocket, value, messageId, userId, extraData)
|
||||
"REGISTRATION" -> handlePerformRegistration(webSocket, value, messageId, userId, extraData)
|
||||
"FUNCTION_CALL" -> handleFunctionCall(webSocket, value, messageId, userId, extraData)
|
||||
else ->
|
||||
{
|
||||
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<*, *>?) {
|
||||
// Registration is automatic
|
||||
webSocket.send(
|
||||
|
@ -12,6 +12,7 @@ class ProgramakerBridgeConfiguration(
|
||||
// val allow_multiple_connections: boolean // No reason for this use case
|
||||
// val icon: ???, // Not supported yet // TODO: Add support
|
||||
) {
|
||||
|
||||
fun serialize(): String {
|
||||
var serializedBlocks = listOf<JSONObject>()
|
||||
if (blocks != null) {
|
||||
@ -34,4 +35,14 @@ class ProgramakerBridgeConfiguration(
|
||||
|
||||
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
|
||||
|
||||
class ProgramakerBridgeConfigurationBlock {
|
||||
fun serialize() : JSONObject {
|
||||
val obj = JSONObject();
|
||||
return obj;
|
||||
}
|
||||
interface ProgramakerBridgeConfigurationBlock {
|
||||
fun serialize() : JSONObject;
|
||||
fun getFunctionName(): String;
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun call(arguments: List<*>)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user