From c9c5752281e6c79e27f2e7ab92a6fb77d1940fbb Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 10 Sep 2020 15:17:05 +0200 Subject: [PATCH] A first stab on Google Device Actions (not working at all yet) --- packaging/android/AndroidManifest.xml | 7 + packaging/android/build.gradle | 2 + .../nymeaapp/NymeaAppHomeControlsService.java | 127 ++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 packaging/android/src/io/guh/nymeaapp/NymeaAppHomeControlsService.java diff --git a/packaging/android/AndroidManifest.xml b/packaging/android/AndroidManifest.xml index 4bbbaaf7..d6469ea1 100644 --- a/packaging/android/AndroidManifest.xml +++ b/packaging/android/AndroidManifest.xml @@ -73,6 +73,13 @@ + + + + + + + diff --git a/packaging/android/build.gradle b/packaging/android/build.gradle index 69d802cd..957ca95f 100644 --- a/packaging/android/build.gradle +++ b/packaging/android/build.gradle @@ -30,6 +30,8 @@ dependencies { compile 'com.google.android.gms:play-services-base:15.0.1' compile 'com.google.firebase:firebase-messaging:18.0.0' compile 'com.google.firebase.messaging.cpp:firebase_messaging_cpp@aar' + implementation 'org.reactivestreams:reactive-streams:1.0.3' + implementation 'io.reactivex.rxjava2:rxjava:2.2.0' } apply plugin: 'com.google.gms.google-services' diff --git a/packaging/android/src/io/guh/nymeaapp/NymeaAppHomeControlsService.java b/packaging/android/src/io/guh/nymeaapp/NymeaAppHomeControlsService.java new file mode 100644 index 00000000..bb31d2b0 --- /dev/null +++ b/packaging/android/src/io/guh/nymeaapp/NymeaAppHomeControlsService.java @@ -0,0 +1,127 @@ +package io.guh.nymeaapp; + +import android.util.Log; +import android.content.Intent; +import android.app.PendingIntent; +import android.net.Uri; +import android.content.Context; +import android.provider.Settings.System; +import android.os.Build; +import android.service.controls.ControlsProviderService; +import android.service.controls.actions.ControlAction; +import android.service.controls.actions.BooleanAction; +import android.service.controls.Control; +import android.service.controls.DeviceTypes; + +import java.util.Random; +import java.util.concurrent.Flow.Publisher; +import java.util.function.Consumer; +import java.util.List; +import java.util.ArrayList; +import io.reactivex.Flowable; +import io.reactivex.processors.ReplayProcessor; +import org.reactivestreams.FlowAdapters; + +public class NymeaAppHomeControlsService extends ControlsProviderService { + + private ReplayProcessor updatePublisher; + private PendingIntent pi; + + @Override + public Publisher createPublisherForAllAvailable() { + Context context = getBaseContext(); + Intent i = new Intent(); +// PendingIntent pi = PendingIntent.getActivity(context, 1, i, PendingIntent.FLAG_UPDATE_CURRENT); + pi = PendingIntent.getActivity(context, 1, i, PendingIntent.FLAG_UPDATE_CURRENT); + List controls = new ArrayList<>(); + Control control = new Control.StatelessBuilder("123", pi) + // Required: The name of the control + .setTitle("TestControl") + // Required: Usually the room where the control is located + .setSubtitle("TestSubtitle") + // Optional: Structure where the control is located, an example would be a house + .setStructure("TestLocation") + // Required: Type of device, i.e., thermostat, light, switch + .setDeviceType(DeviceTypes.TYPE_LIGHT) // For example, DeviceTypes.TYPE_THERMOSTAT + .build(); + controls.add(control); + // Create more controls here if needed and add it to the ArrayList + + // Uses the RxJava 2 library + return FlowAdapters.toFlowPublisher(Flowable.fromIterable(controls)); + } + + + @Override + public Publisher createPublisherFor(List controlIds) { + Context context = getBaseContext(); + /* Fill in details for the activity related to this device. On long press, + * this Intent will be launched in a bottomsheet. Please design the activity + * accordingly to fit a more limited space (about 2/3 screen height). + */ + Intent i = new Intent(); + PendingIntent pi = PendingIntent.getActivity(context, 1, i, PendingIntent.FLAG_UPDATE_CURRENT); + + updatePublisher = ReplayProcessor.create(); + + // For each controlId in controlIds + + if (controlIds.contains(123)) { + Control control = new Control.StatefulBuilder("123", pi) + // Required: The name of the control + .setTitle("TestTitle") + // Required: Usually the room where the control is located + .setSubtitle("TestSubTitle") + // Optional: Structure where the control is located, an example would be a house + .setStructure("TestStructure") + // Required: Type of device, i.e., thermostat, light, switch + .setDeviceType(DeviceTypes.TYPE_LIGHT) // For example, DeviceTypes.TYPE_THERMOSTAT + // Required: Current status of the device + .setStatus(Control.STATUS_OK) // For example, Control.STATUS_OK + .build(); + + updatePublisher.onNext(control); + } + // Uses the Reactive Streams API + return FlowAdapters.toFlowPublisher(updatePublisher); + } + + @Override + public void performControlAction(String controlId, ControlAction action, Consumer consumer) { + /* First, locate the control identified by the controlId. Once it is located, you can + * interpret the action appropriately for that specific device. For instance, the following + * assumes that the controlId is associated with a light, and the light can be turned on + * or off. + */ + if (action instanceof BooleanAction) { + + // Inform SystemUI that the action has been received and is being processed + consumer.accept(ControlAction.RESPONSE_OK); + + BooleanAction bAction = (BooleanAction) action; + // In this example, action.getNewState() will have the requested action: true for “On”, + // false for “Off”. + + /* This is where application logic/network requests would be invoked to update the state of + * the device. + * After updating, the application should use the publisher to update SystemUI with the new + * state. + */ + Control control = new Control.StatefulBuilder("123", pi) + // Required: The name of the control + .setTitle("TestControl") + // Required: Usually the room where the control is located + .setSubtitle("TestSubTitle") + // Optional: Structure where the control is located, an example would be a house + .setStructure("TestStructure") + // Required: Type of device, i.e., thermostat, light, switch + .setDeviceType(DeviceTypes.TYPE_LIGHT) // For example, DeviceTypes.TYPE_THERMOSTAT + // Required: Current status of the device + .setStatus(Control.STATUS_OK) // For example, Control.STATUS_OK + .build(); + + // This is the publisher the application created during the call to createPublisherFor() + updatePublisher.onNext(control); + } + } +}