diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1df4bcb..fb8b0c5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,10 +7,13 @@ android:glEsVersion="0x00020000" android:required="true" /> + + + blockList; @@ -25,6 +24,18 @@ public class BridgeBlockListBuilder { return this.addOperation(new OperationBlockDefinition(id, message, arguments, operation)); } + public BridgeBlockListBuilder addTrigger(String id, String message, + List arguments, + String key) { + return this.addTrigger(new TriggerBlockDefinition(id, message, arguments, key)); + } + + private BridgeBlockListBuilder addTrigger(TriggerBlockDefinition block) { + this.blockList.add(block); + + return this; + } + private BridgeBlockListBuilder addOperation(ProgramakerBridgeConfigurationBlock block) { this.blockList.add(block); 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 4883875..8e6412f 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 @@ -53,8 +53,8 @@ public class DefaultAndroidBlocks { notificationManager.createNotificationChannel(vibrationChannel); } - - return new BridgeBlockListBuilder() + // OPERATIONS + BridgeBlockListBuilder builder = new BridgeBlockListBuilder() // Notifications .addOperation( "notifications_new", @@ -109,7 +109,6 @@ public class DefaultAndroidBlocks { pattern[i * 2] = VIBRATION_ACTIVE_TIME; pattern[i * 2 + 1] = VIBRATION_REST_TIME; } - // pattern[0] = 0; // Start immediately Notification notif = new NotificationCompat .Builder(ctx, ProgramakerBridgeService.BridgeUserVibrationNotificationChannel) @@ -145,5 +144,22 @@ public class DefaultAndroidBlocks { } ) ; + + // Signal blocks + builder + .addTrigger( + "on_wifi_connected", + "When WIFI connects", + Collections.emptyList(),// TODO: Save content to variable + "on_wifi_connected" + ) + .addTrigger( + "on_wifi_disconnected", + "When WIFI connection is lost", + Collections.emptyList(), + "on_wifi_disconnected" + ); + + return builder; } } 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 4d48d5c..95503e7 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 @@ -13,7 +13,7 @@ import org.json.JSONObject; import java.util.List; class OperationBlockDefinition implements ProgramakerBridgeConfigurationBlock { - private final static String LogTag = "PM OpBlockDefinition"; + private final static String LogTag = "PM OpBlockDef"; private final String id; private final String message; 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 new file mode 100644 index 0000000..fc989ca --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/blocks/TriggerBlockDefinition.java @@ -0,0 +1,67 @@ +package com.codigoparallevar.minicards.bridge.blocks; + +import android.util.Log; + +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 TriggerBlockDefinition implements ProgramakerBridgeConfigurationBlock { + private final static String LogTag = "PM TriggerBlockDef"; + + private final String id; + private final String message; + private final List args; + private final String key; + + public TriggerBlockDefinition(String id, String message, List args, + String key) { + this.id = id; + this.message = message; + this.args = args; + this.key = key; + } + + @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("arguments", arguments); + obj.put("save_to", JSONObject.NULL); + obj.put("expected_value", JSONObject.NULL); + obj.put("block_type", "trigger"); + obj.put("key", this.key); + obj.put("subkey", 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 { + throw new IllegalArgumentException("Non executable block"); + } +} diff --git a/app/src/main/java/com/codigoparallevar/minicards/bridge/helpers/StatusStreamer.java b/app/src/main/java/com/codigoparallevar/minicards/bridge/helpers/StatusStreamer.java new file mode 100644 index 0000000..41b90a9 --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/helpers/StatusStreamer.java @@ -0,0 +1,78 @@ +package com.codigoparallevar.minicards.bridge.helpers; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.net.wifi.SupplicantState; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Build; + +import com.programaker.bridge.ProgramakerBridge; + +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; + +public class StatusStreamer { + private final Context ctx; + private final ProgramakerBridge bridge; + private WifiNetworkCallbackReceiver networkCallback = null; + + public StatusStreamer(Context ctx, ProgramakerBridge bridge) { + this.ctx = ctx; + this.bridge = bridge; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + this.networkCallback = new WifiNetworkCallbackReceiver(this); + } + } + + public void run() { + + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) { + return; + } + + NetworkRequest.Builder builder = new NetworkRequest.Builder(); + builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); + + ConnectivityManager connManager = (ConnectivityManager) this.ctx.getSystemService(Context.CONNECTIVITY_SERVICE); + assert connManager != null; + + connManager.registerNetworkCallback(builder.build(), this.networkCallback); + } + + public void stop() { + ConnectivityManager connManager = (ConnectivityManager) this.ctx.getSystemService(Context.CONNECTIVITY_SERVICE); + assert connManager != null; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + connManager.unregisterNetworkCallback(this.networkCallback); + } + } + + public void onWifiNetworkConnected(Network network) { + WifiManager wifiManager = (WifiManager) this.ctx.getSystemService(Context.WIFI_SERVICE); + WifiInfo wifiInfo; + + wifiInfo = wifiManager.getConnectionInfo(); + String ssid = ""; + if (wifiInfo.getSupplicantState() == SupplicantState.COMPLETED) { + // This might not return the SSID in case the user has not granted location permissions to the app + // see: https://stackoverflow.com/a/54446042 + ssid = wifiInfo.getSSID(); + } + + Map data = new HashMap<>(); + data.put("ssid", ssid); + + this.bridge.sendSignal("on_wifi_connected", new JSONObject(data)); + } + + public void onWifiNetworkDisconnected() { + this.bridge.sendSignal("on_wifi_disconnected", new JSONObject()); + } +} diff --git a/app/src/main/java/com/codigoparallevar/minicards/bridge/helpers/WifiNetworkCallbackReceiver.java b/app/src/main/java/com/codigoparallevar/minicards/bridge/helpers/WifiNetworkCallbackReceiver.java new file mode 100644 index 0000000..b958397 --- /dev/null +++ b/app/src/main/java/com/codigoparallevar/minicards/bridge/helpers/WifiNetworkCallbackReceiver.java @@ -0,0 +1,34 @@ +package com.codigoparallevar.minicards.bridge.helpers; + +import android.net.ConnectivityManager; +import android.net.Network; +import android.os.Build; + +import androidx.annotation.RequiresApi; + +@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) +class WifiNetworkCallbackReceiver extends ConnectivityManager.NetworkCallback { + private final StatusStreamer streamer; + + public WifiNetworkCallbackReceiver(StatusStreamer statusStreamer) { + this.streamer = statusStreamer; + } + + @Override + public void onAvailable(Network network) { + super.onAvailable(network); + this.streamer.onWifiNetworkConnected(network); + } + + @Override + public void onUnavailable() { + super.onUnavailable(); + this.streamer.onWifiNetworkDisconnected(); + } + + @Override + public void onLost(Network network) { + super.onLost(network); + this.streamer.onWifiNetworkDisconnected(); + } +} diff --git a/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt b/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt index fa05cf7..6fe418e 100644 --- a/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt +++ b/app/src/main/java/com/programaker/bridge/ProgramakerBridge.kt @@ -173,4 +173,21 @@ class ProgramakerBridge( fun stop() { webSocket?.close(1000, null) } + + fun sendSignal(key: String, content: JSONObject) { + if (webSocket == null) { + Log.w(LogTag, "Cannot send signal (key=$key) on closed channel") + return + } + + webSocket!!.send(JSONObject(hashMapOf( + "type" to "NOTIFICATION", + "key" to key, + "subkey" to JSONObject.NULL, + "to_user" to JSONObject.NULL, + "value" to content, + "content" to content + ) + ).toString()) + } } \ No newline at end of file