From a4aff448188a019f3e070296c17e28cf3c225adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Mart=C3=ADnez=20Portela?= Date: Thu, 28 May 2020 17:35:11 +0200 Subject: [PATCH] Add support for getters. As example getters for wifi connection (boolean) and wifi SSID (string) were added. --- .../bridge/blocks/BridgeBlockListBuilder.java | 26 +++++--- .../bridge/blocks/DefaultAndroidBlocks.java | 49 +++++++++++++- .../bridge/blocks/GetterBlockDefinition.java | 65 +++++++++++++++++++ .../blocks/OperationBlockDefinition.java | 10 +-- .../bridge/blocks/TriggerBlockDefinition.java | 2 +- .../minicards/types/functional/Function.java | 5 ++ .../minicards/utils/Serializations.java | 56 ++++++++++++++++ .../programaker/bridge/ProgramakerBridge.kt | 5 +- .../bridge/ProgramakerBridgeConfiguration.kt | 5 +- .../ProgramakerBridgeConfigurationBlock.kt | 6 +- app/src/main/res/values/strings.xml | 4 +- 11 files changed, 208 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/GetterBlockDefinition.java create mode 100644 app/src/main/java/com/codigoparallevar/minicards/types/functional/Function.java diff --git a/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/BridgeBlockListBuilder.java b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/BridgeBlockListBuilder.java index 2f098d9..c8e911c 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/BridgeBlockListBuilder.java +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/BridgeBlockListBuilder.java @@ -1,6 +1,6 @@ package com.codigoparallevar.minicards.bridge.blocks; -import com.codigoparallevar.minicards.types.functional.Consumer; +import com.codigoparallevar.minicards.types.functional.Function; import com.programaker.bridge.ProgramakerBridgeConfigurationBlock; import java.util.LinkedList; @@ -20,10 +20,26 @@ public class BridgeBlockListBuilder { public BridgeBlockListBuilder addOperation(String id, String message, List arguments, - Consumer> operation) { + Function, Void> operation) { return this.addOperation(new OperationBlockDefinition(id, message, arguments, operation)); } + private BridgeBlockListBuilder addOperation(ProgramakerBridgeConfigurationBlock block) { + this.blockList.add(block); + return this; + } + + public BridgeBlockListBuilder addGetter(String id, String message, + List arguments, + Function, ?> operation) { + return this.addGetter(new GetterBlockDefinition(id, message, arguments, operation)); + } + + private BridgeBlockListBuilder addGetter(ProgramakerBridgeConfigurationBlock block) { + this.blockList.add(block); + return this; + } + public BridgeBlockListBuilder addTrigger(String id, String message, List arguments, String key) { @@ -35,10 +51,4 @@ public class BridgeBlockListBuilder { return this; } - - private BridgeBlockListBuilder addOperation(ProgramakerBridgeConfigurationBlock block) { - this.blockList.add(block); - - return this; - } } diff --git a/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/DefaultAndroidBlocks.java b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/DefaultAndroidBlocks.java index 8e6412f..1fa4c25 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/DefaultAndroidBlocks.java +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/DefaultAndroidBlocks.java @@ -4,6 +4,9 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; +import android.net.wifi.SupplicantState; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.os.Build; import android.os.VibrationEffect; import android.os.Vibrator; @@ -80,6 +83,8 @@ public class DefaultAndroidBlocks { int notificationId = notificationRandom.nextInt(); notificationManager.notify(notificationId, notif); + + return null; } ) .addOperation( @@ -88,6 +93,8 @@ public class DefaultAndroidBlocks { Collections.emptyList(), (List params) -> { notificationManager.cancelAll(); + + return null; } ) .addOperation( @@ -141,21 +148,59 @@ public class DefaultAndroidBlocks { // Remove notification notificationManager.cancel(notificationId); }).start(); + + return null; } ) ; + // GETTERS + builder.addGetter( + "wifi_is_connected", + "Is WIFI connected?", + Collections.emptyList(), + (List params) -> { + WifiManager wifiManager = (WifiManager) ctx.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + assert wifiManager != null; + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + + if (wifiInfo == null) { + return false; + } + + return wifiInfo.getSupplicantState() == SupplicantState.COMPLETED; + }) + .addGetter( + "wifi_get_ssid", + "Get WIFI SSID", + Collections.emptyList(), + (List params) -> { + WifiManager wifiManager = (WifiManager) ctx.getApplicationContext().getSystemService(Context.WIFI_SERVICE); + assert wifiManager != null; + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + + if (wifiInfo == null) { + return null; + } + + if (wifiInfo.getSupplicantState() != SupplicantState.COMPLETED) { + return null; + } + + return wifiInfo.getSSID(); + }); + // Signal blocks builder .addTrigger( "on_wifi_connected", - "When WIFI connects", + "When WIFI connection is ESTABLISHED", Collections.emptyList(),// TODO: Save content to variable "on_wifi_connected" ) .addTrigger( "on_wifi_disconnected", - "When WIFI connection is lost", + "When WIFI connection is LOST", Collections.emptyList(), "on_wifi_disconnected" ); diff --git a/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/GetterBlockDefinition.java b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/GetterBlockDefinition.java new file mode 100644 index 0000000..816ef54 --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/GetterBlockDefinition.java @@ -0,0 +1,65 @@ +package com.codigoparallevar.minicards.bridge.blocks; + +import android.util.Log; + +import com.codigoparallevar.minicards.types.functional.Function; +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 GetterBlockDefinition implements ProgramakerBridgeConfigurationBlock { + private final static String LogTag = "PM GetterBlockDef"; + + private final String id; + private final String message; + private final List args; + private final Function, ?> operation; + + public GetterBlockDefinition(String id, String message, List args, Function, ?> 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", "getter"); + obj.put("block_result_type", JSONObject.NULL); // TODO: Properly declare type + 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 Object call(@NotNull List arguments) throws Exception { + return this.operation.apply(arguments); + } +} diff --git a/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/OperationBlockDefinition.java b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/OperationBlockDefinition.java index 95503e7..caa4b82 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/OperationBlockDefinition.java +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/OperationBlockDefinition.java @@ -2,7 +2,7 @@ package com.codigoparallevar.minicards.bridge.blocks; import android.util.Log; -import com.codigoparallevar.minicards.types.functional.Consumer; +import com.codigoparallevar.minicards.types.functional.Function; import com.programaker.bridge.ProgramakerBridgeConfigurationBlock; import org.jetbrains.annotations.NotNull; @@ -18,9 +18,9 @@ class OperationBlockDefinition implements ProgramakerBridgeConfigurationBlock { private final String id; private final String message; private final List args; - private final Consumer> operation; + private final Function, Void> operation; - public OperationBlockDefinition(String id, String message, List args, Consumer> operation) { + public OperationBlockDefinition(String id, String message, List args, Function, Void> operation) { this.id = id; this.message = message; this.args = args; @@ -59,7 +59,7 @@ class OperationBlockDefinition implements ProgramakerBridgeConfigurationBlock { } @Override - public void call(@NotNull List arguments) throws Exception { - this.operation.apply(arguments); + public Object call(@NotNull List arguments) throws Exception { + return this.operation.apply(arguments); } } diff --git a/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/TriggerBlockDefinition.java b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/TriggerBlockDefinition.java index fc989ca..53a3a15 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/TriggerBlockDefinition.java +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/TriggerBlockDefinition.java @@ -61,7 +61,7 @@ class TriggerBlockDefinition implements ProgramakerBridgeConfigurationBlock { } @Override - public void call(@NotNull List arguments) throws Exception { + public Object call(@NotNull List arguments) throws Exception { throw new IllegalArgumentException("Non executable block"); } } diff --git a/app/src/main/java/com/codigoparallevar/minicards/types/functional/Function.java b/app/src/main/java/com/codigoparallevar/minicards/types/functional/Function.java new file mode 100644 index 0000000..bebbead --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/types/functional/Function.java @@ -0,0 +1,5 @@ +package com.codigoparallevar.minicards.types.functional; + +public interface Function { + U apply(T param) throws Exception; +} diff --git a/app/src/main/java/com/codigoparallevar/minicards/utils/Serializations.java b/app/src/main/java/com/codigoparallevar/minicards/utils/Serializations.java index 1119a08..858a959 100644 --- a/app/src/main/java/com/codigoparallevar/minicards/utils/Serializations.java +++ b/app/src/main/java/com/codigoparallevar/minicards/utils/Serializations.java @@ -1,6 +1,10 @@ package com.codigoparallevar.minicards.utils; +import android.util.Log; + +import org.jetbrains.annotations.NotNull; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import java.util.List; @@ -8,6 +12,8 @@ import java.util.Map; public class Serializations { + private static final String LogTag = "Serialization"; + public static JSONArray serialize(List> data) { JSONArray array = new JSONArray(); for (Map dict : data) { @@ -16,4 +22,54 @@ public class Serializations { return array; } + + @NotNull + public static Object blindSerialize(Object obj) { + if (obj == null) { + return JSONObject.NULL; + } + // No serialization needed + else if (obj.getClass().isPrimitive() || + obj instanceof String || + obj instanceof JSONObject || + obj instanceof JSONArray) { + + return obj; + } + + try { + return _blindSerialize(obj); + } + catch (Exception ex) { + Log.w(LogTag, "Error serialization: " + obj, ex); + return obj; + } + } + + private static Object _blindSerialize(Object obj) throws JSONException { + if (obj instanceof Map) { + Map objMap = (Map) obj; + + JSONObject map = new JSONObject(); + + for (Map.Entry entry : objMap.entrySet()) { + map.put(entry.getKey().toString(), blindSerialize(entry.getValue())); + } + + return map; + } + else if (obj instanceof Iterable) { + Iterable objIt = (Iterable) obj; + + JSONArray arr = new JSONArray(); + for (Object val : objIt) { + arr.put(blindSerialize(val)); + } + return arr; + } + else { + // Unknown case + return obj; + } + } } diff --git a/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt b/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt index 6fe418e..417f9ba 100644 --- a/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt +++ b/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt @@ -1,6 +1,7 @@ package com.programaker.bridge import android.util.Log +import com.codigoparallevar.minicards.utils.Serializations import com.google.gson.Gson import okhttp3.* import okio.ByteString @@ -113,9 +114,9 @@ class ProgramakerBridge( val arguments = value.get("arguments") as List<*> try { - config.callFunction(functionName, arguments) + var result = config.callFunction(functionName, arguments) + result = Serializations.blindSerialize(result) - val result = JSONObject.NULL webSocket.send( JSONObject( hashMapOf( diff --git a/app/src/main/java/com/programaker/bridge/ProgramakerBridgeConfiguration.kt b/app/src/main/java/com/programaker/bridge/ProgramakerBridgeConfiguration.kt index 6b1a1d1..164518c 100644 --- a/app/src/main/java/com/programaker/bridge/ProgramakerBridgeConfiguration.kt +++ b/app/src/main/java/com/programaker/bridge/ProgramakerBridgeConfiguration.kt @@ -36,9 +36,10 @@ class ProgramakerBridgeConfiguration( return wrapper.toString() } - fun callFunction(functionName: String, arguments: List<*>) { + fun callFunction(functionName: String, arguments: List<*>): Any { for (block in blocks) { - if (block.getFunctionName() == functionName) { + val blockFunction = block.getFunctionName() + if (blockFunction != null && blockFunction == functionName) { return block.call(arguments) } } diff --git a/app/src/main/java/com/programaker/bridge/ProgramakerBridgeConfigurationBlock.kt b/app/src/main/java/com/programaker/bridge/ProgramakerBridgeConfigurationBlock.kt index 440dcfc..294c270 100644 --- a/app/src/main/java/com/programaker/bridge/ProgramakerBridgeConfigurationBlock.kt +++ b/app/src/main/java/com/programaker/bridge/ProgramakerBridgeConfigurationBlock.kt @@ -3,9 +3,9 @@ package com.programaker.bridge import org.json.JSONObject interface ProgramakerBridgeConfigurationBlock { - fun serialize() : JSONObject; - fun getFunctionName(): String; + fun serialize() : JSONObject + fun getFunctionName(): String @Throws(Exception::class) - fun call(arguments: List<*>) + fun call(arguments: List<*>): Any } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 29aa7be..3ef5b93 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,9 +19,9 @@ Back to card Deck Go back Programaker bridge not started - Programaker bridge starting + Programaker bridge connecting... Programaker bridge online - Programaker bridge failed, restarting... + Programaker bridge reconnecting... Programaker bridge stopped Stop bridge Start bridge