aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com>2023-07-06 10:36:07 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2023-07-06 10:36:07 +0000
commit55dfd408b232c9d46cfff5372df4014191d5708f (patch)
tree20d79915c5dd258b2b9ea038d3ec1a1ed1adbe8c
parentfd0ff4b94116ce16bfb19ecbf3ff50ba0b6f6e90 (diff)
parentfce69f27571cc0f9ad2a5ce994ac0712a5fa4533 (diff)
downloadsupport-55dfd408b232c9d46cfff5372df4014191d5708f.tar.gz
Merge "Add support for resource only watch face runtimes" into androidx-main
-rw-r--r--wear/watchface/watchface-client-guava/api/current.txt4
-rw-r--r--wear/watchface/watchface-client-guava/api/restricted_current.txt4
-rw-r--r--wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt47
-rw-r--r--wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt32
-rw-r--r--wear/watchface/watchface-client/api/current.txt3
-rw-r--r--wear/watchface/watchface-client/api/restricted_current.txt3
-rw-r--r--wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt3
-rw-r--r--wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/SerializationTest.kt23
-rw-r--r--wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt70
-rw-r--r--wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt41
-rw-r--r--wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt58
-rw-r--r--wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt41
-rw-r--r--wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/control/data/WallpaperInteractiveWatchFaceInstanceParams.java10
-rw-r--r--wear/watchface/watchface-guava/api/current.txt6
-rw-r--r--wear/watchface/watchface-guava/api/restricted_current.txt6
-rw-r--r--wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRenderer2Test.kt5
-rw-r--r--wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRendererTest.kt5
-rw-r--r--wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRenderer2Test.kt5
-rw-r--r--wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRendererTest.kt5
-rw-r--r--wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableWatchFaceServiceTest.kt93
-rw-r--r--wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/WatchFaceControlTestService.kt29
-rw-r--r--wear/watchface/watchface-guava/src/main/java/androidx/wear/watchface/ListenableWatchFaceService.kt55
-rw-r--r--wear/watchface/watchface/api/current.txt12
-rw-r--r--wear/watchface/watchface/api/restricted_current.txt12
-rw-r--r--wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt9
-rw-r--r--wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt3
-rw-r--r--wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt292
-rw-r--r--wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt15
-rw-r--r--wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt3
-rw-r--r--wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt46
30 files changed, 871 insertions, 69 deletions
diff --git a/wear/watchface/watchface-client-guava/api/current.txt b/wear/watchface/watchface-client-guava/api/current.txt
index 471cc7f1a69..9c9689dc854 100644
--- a/wear/watchface/watchface-client-guava/api/current.txt
+++ b/wear/watchface/watchface-client-guava/api/current.txt
@@ -7,6 +7,7 @@ package androidx.wear.watchface.client {
method @Deprecated public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
method public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(String id, android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
method public static final com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+ method public static final com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceRuntimeControlClientAsync(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName);
method @Deprecated public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName);
method public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient();
method public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId);
@@ -19,15 +20,18 @@ package androidx.wear.watchface.client {
public static final class ListenableWatchFaceControlClient.Companion {
method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceRuntimeControlClientAsync(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName);
}
public final class ListenableWatchFaceMetadataClient {
method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+ method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String resourceOnlyWatchFacePackageName);
field public static final androidx.wear.watchface.client.ListenableWatchFaceMetadataClient.Companion Companion;
}
public static final class ListenableWatchFaceMetadataClient.Companion {
method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String resourceOnlyWatchFacePackageName);
}
}
diff --git a/wear/watchface/watchface-client-guava/api/restricted_current.txt b/wear/watchface/watchface-client-guava/api/restricted_current.txt
index 471cc7f1a69..9c9689dc854 100644
--- a/wear/watchface/watchface-client-guava/api/restricted_current.txt
+++ b/wear/watchface/watchface-client-guava/api/restricted_current.txt
@@ -7,6 +7,7 @@ package androidx.wear.watchface.client {
method @Deprecated public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
method public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(String id, android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, int surfaceWidth, int surfaceHeight);
method public static final com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+ method public static final com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceRuntimeControlClientAsync(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName);
method @Deprecated public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName);
method public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient();
method public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId);
@@ -19,15 +20,18 @@ package androidx.wear.watchface.client {
public static final class ListenableWatchFaceControlClient.Companion {
method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceControlClient(android.content.Context context, String watchFacePackageName);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.ListenableWatchFaceControlClient> createWatchFaceRuntimeControlClientAsync(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName);
}
public final class ListenableWatchFaceMetadataClient {
method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+ method public static com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String resourceOnlyWatchFacePackageName);
field public static final androidx.wear.watchface.client.ListenableWatchFaceMetadataClient.Companion Companion;
}
public static final class ListenableWatchFaceMetadataClient.Companion {
method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> create(android.content.Context context, android.content.ComponentName watchFaceName);
+ method public com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.client.WatchFaceMetadataClient> createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String resourceOnlyWatchFacePackageName);
}
}
diff --git a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt
index e3de1528d32..11fa95bb7c9 100644
--- a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt
+++ b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceControlClient.kt
@@ -21,6 +21,8 @@ import android.content.Context
import androidx.concurrent.futures.ResolvableFuture
import androidx.core.util.Consumer
import androidx.wear.watchface.Renderer
+import androidx.wear.watchface.WatchFaceRuntimeService
+import androidx.wear.watchface.client.WatchFaceControlClient.Companion.createWatchFaceControlClient
import androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException
import androidx.wear.watchface.complications.data.ComplicationData
import androidx.wear.watchface.style.UserStyleData
@@ -119,6 +121,51 @@ public open class ListenableWatchFaceControlClient(
)
)
}
+
+ /**
+ * Similar [createWatchFaceControlClient] this constructs a [WatchFaceControlClient] which
+ * attempts to connect to the watch face runtime in the android package
+ * [runtimePackageName].
+ *
+ * A watch face runtime is a special type of watch face, which renders a watch face
+ * described by resources in another package [resourceOnlyWatchFacePackageName].
+ *
+ * Note only one watch face definition per resource only watch face package is supported.
+ *
+ * Currently Wear OS only supports the runtime for the Android Watch Face Format (see
+ * https://developer.android.com/training/wearables/wff for more details).
+ *
+ * @param context Calling application's [Context].
+ * @param runtimePackageName The name of the package containing the watch face runtime's
+ * control service to bind to.
+ * @param resourceOnlyWatchFacePackageName The name of the package from which to load the
+ * resource only watch face. This is exposed to the runtime via the
+ * `resourceOnlyWatchFacePackageName` parameter passed to
+ * [WatchFaceRuntimeService.createUserStyleSchema],
+ * [WatchFaceRuntimeService.createComplicationSlotsManager],
+ * [WatchFaceRuntimeService.createUserStyleFlavors] and
+ * [WatchFaceRuntimeService.createWatchFace]).
+ * @return [ListenableFuture]<[ListenableWatchFaceControlClient]> which on success resolves
+ * to a [ListenableWatchFaceControlClient] or throws a [ServiceNotBoundException] if the
+ * watch face control service can not be bound.
+ */
+ @JvmStatic
+ public fun createWatchFaceRuntimeControlClientAsync(
+ context: Context,
+ runtimePackageName: String,
+ resourceOnlyWatchFacePackageName: String
+ ): ListenableFuture<ListenableWatchFaceControlClient> =
+ launchFutureCoroutine(
+ "ListenableWatchFaceControlClient.createWatchFaceRuntimeControlClient",
+ ) {
+ ListenableWatchFaceControlClient(
+ WatchFaceControlClient.createWatchFaceRuntimeControlClient(
+ context,
+ runtimePackageName,
+ resourceOnlyWatchFacePackageName
+ )
+ )
+ }
}
@Suppress("DEPRECATION")
diff --git a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt
index 6d25efd16e9..5a49cbb44fb 100644
--- a/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt
+++ b/wear/watchface/watchface-client-guava/src/main/java/androidx/wear/watchface/client/ListenableWatchFaceMetadataClient.kt
@@ -53,6 +53,38 @@ public class ListenableWatchFaceMetadataClient private constructor() {
WatchFaceMetadataClient.Companion.ParserProvider()
)
+ /**
+ * Constructs a [WatchFaceMetadataClient] for fetching metadata for the specified resource
+ * only watch face runtime. A resource only watch face runtime, is a special kind of watch
+ * face that is the runtime for a watch face defined by another package that contains only
+ * resources and no executable code.
+ *
+ * @param context Calling application's [Context].
+ * @param watchFaceName The [ComponentName] of the watch face to fetch meta data from.
+ * @param resourceOnlyWatchFacePackageName The package the runtime should load the resources
+ * from.
+ * @return A [ListenableFuture] which resolves with [WatchFaceMetadataClient] if there is
+ * one, otherwise it throws a [ServiceNotBoundException] if the underlying watch face
+ * control service can not be bound or a [ServiceStartFailureException] if the watch face
+ * dies during startup.
+ */
+ @Suppress("AsyncSuffixFuture")
+ @JvmStatic
+ public fun createForRuntime(
+ context: Context,
+ watchFaceName: ComponentName,
+ resourceOnlyWatchFacePackageName: String
+ ) =
+ ListenableWatchFaceControlClient.launchFutureCoroutine(
+ "ListenableWatchFaceMetadataClient.create"
+ ) {
+ WatchFaceMetadataClient.createForRuntime(
+ context,
+ watchFaceName,
+ resourceOnlyWatchFacePackageName
+ )
+ }
+
internal fun createImpl(
context: Context,
intent: Intent,
diff --git a/wear/watchface/watchface-client/api/current.txt b/wear/watchface/watchface-client/api/current.txt
index 41cfe987217..9e3a9b69065 100644
--- a/wear/watchface/watchface-client/api/current.txt
+++ b/wear/watchface/watchface-client/api/current.txt
@@ -206,6 +206,7 @@ package androidx.wear.watchface.client {
method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public default androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(String id, android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public static suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
+ method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public static suspend Object? createWatchFaceRuntimeControlClient(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient() throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId) throws android.os.RemoteException;
@@ -217,6 +218,7 @@ package androidx.wear.watchface.client {
public static final class WatchFaceControlClient.Companion {
method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
+ method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public suspend Object? createWatchFaceRuntimeControlClient(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
}
public static final class WatchFaceControlClient.ServiceNotBoundException extends java.lang.Exception {
@@ -244,6 +246,7 @@ package androidx.wear.watchface.client {
public static final class WatchFaceMetadataClient.Companion {
method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? create(android.content.Context context, android.content.ComponentName watchFaceName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient>) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
+ method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String runtimePackage, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient>) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
}
public static final class WatchFaceMetadataClient.ServiceNotBoundException extends java.lang.Exception {
diff --git a/wear/watchface/watchface-client/api/restricted_current.txt b/wear/watchface/watchface-client/api/restricted_current.txt
index 41cfe987217..9e3a9b69065 100644
--- a/wear/watchface/watchface-client/api/restricted_current.txt
+++ b/wear/watchface/watchface-client/api/restricted_current.txt
@@ -206,6 +206,7 @@ package androidx.wear.watchface.client {
method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public default androidx.wear.watchface.client.HeadlessWatchFaceClient? createHeadlessWatchFaceClient(String id, android.content.ComponentName watchFaceName, androidx.wear.watchface.client.DeviceConfig deviceConfig, @Px int surfaceWidth, @Px int surfaceHeight) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public static suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
+ method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public static suspend Object? createWatchFaceRuntimeControlClient(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
method @Deprecated @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public java.util.Map<java.lang.Integer,androidx.wear.watchface.client.DefaultComplicationDataSourcePolicyAndType> getDefaultComplicationDataSourcePoliciesAndType(android.content.ComponentName watchFaceName) throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.EditorServiceClient getEditorServiceClient() throws android.os.RemoteException;
method @kotlin.jvm.Throws(exceptionClasses=RemoteException::class) public androidx.wear.watchface.client.InteractiveWatchFaceClient? getInteractiveWatchFaceClientInstance(String instanceId) throws android.os.RemoteException;
@@ -217,6 +218,7 @@ package androidx.wear.watchface.client {
public static final class WatchFaceControlClient.Companion {
method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public suspend Object? createWatchFaceControlClient(android.content.Context context, String watchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
+ method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class}) public suspend Object? createWatchFaceRuntimeControlClient(android.content.Context context, String runtimePackageName, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceControlClient>) throws androidx.wear.watchface.client.WatchFaceControlClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceControlClient.ServiceStartFailureException;
}
public static final class WatchFaceControlClient.ServiceNotBoundException extends java.lang.Exception {
@@ -244,6 +246,7 @@ package androidx.wear.watchface.client {
public static final class WatchFaceMetadataClient.Companion {
method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? create(android.content.Context context, android.content.ComponentName watchFaceName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient>) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
+ method @kotlin.jvm.Throws(exceptionClasses={ServiceNotBoundException::class, ServiceStartFailureException::class, PackageManager.NameNotFoundException::class}) public suspend Object? createForRuntime(android.content.Context context, android.content.ComponentName watchFaceName, String runtimePackage, kotlin.coroutines.Continuation<? super androidx.wear.watchface.client.WatchFaceMetadataClient>) throws android.content.pm.PackageManager.NameNotFoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceNotBoundException, androidx.wear.watchface.client.WatchFaceMetadataClient.ServiceStartFailureException;
}
public static final class WatchFaceMetadataClient.ServiceNotBoundException extends java.lang.Exception {
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt
index d6c8fb57650..bf838f8c95e 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/HeadlessWatchFaceClientTest.kt
@@ -61,7 +61,8 @@ abstract class HeadlessWatchFaceClientTestBase {
context,
Intent(context, WatchFaceControlTestService::class.java).apply {
action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
- }
+ },
+ resourceOnlyWatchFacePackageName = null
)
}
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/SerializationTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/SerializationTest.kt
index ff8ba4b1449..26eb17a8026 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/SerializationTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/SerializationTest.kt
@@ -22,6 +22,7 @@ import android.graphics.Color
import android.graphics.Rect
import android.os.Build
import android.os.Parcel
+import android.support.wearable.watchface.SharedMemoryImage
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -29,8 +30,8 @@ import androidx.wear.watchface.ComplicationSlotBoundsType
import androidx.wear.watchface.DrawMode
import androidx.wear.watchface.RenderParameters
import androidx.wear.watchface.client.ComplicationSlotState
+import androidx.wear.watchface.client.EditorState
import androidx.wear.watchface.client.WatchFaceId
-import androidx.wear.watchface.client.asApiEditorState
import androidx.wear.watchface.complications.SystemDataSources
import androidx.wear.watchface.complications.data.ComplicationType
import androidx.wear.watchface.complications.data.LongTextComplicationData
@@ -52,6 +53,26 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+// FIXME: We shouldn't have to define this here, we should be able to import it.
+internal fun EditorStateWireFormat.asApiEditorState(): EditorState {
+ return EditorState(
+ WatchFaceId(watchFaceInstanceId ?: ""),
+ UserStyleData(userStyle.mUserStyle),
+ previewComplicationData.associateBy(
+ { it.id },
+ { it.complicationData.toApiComplicationData() }
+ ),
+ commitChanges,
+ previewImageBundle?.let {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
+ SharedMemoryImage.ashmemReadImageBundle(it)
+ } else {
+ null
+ }
+ }
+ )
+}
+
/** Tests that we can deserialize golden resources correctly to ensure backwards compatibility. */
@RunWith(AndroidJUnit4::class)
@MediumTest
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
index d758df72b21..52e8c101c68 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/TestWatchFaceServices.kt
@@ -34,6 +34,7 @@ import androidx.wear.watchface.ComplicationTapFilter
import androidx.wear.watchface.RenderParameters
import androidx.wear.watchface.Renderer
import androidx.wear.watchface.WatchFace
+import androidx.wear.watchface.WatchFaceRuntimeService
import androidx.wear.watchface.WatchFaceService
import androidx.wear.watchface.WatchFaceType
import androidx.wear.watchface.WatchState
@@ -54,6 +55,7 @@ import androidx.wear.watchface.samples.ExampleCanvasAnalogWatchFaceService.Compa
import androidx.wear.watchface.samples.ExampleOpenGLBackgroundInitWatchFaceService
import androidx.wear.watchface.samples.R
import androidx.wear.watchface.style.CurrentUserStyleRepository
+import androidx.wear.watchface.style.UserStyleFlavors
import androidx.wear.watchface.style.UserStyleSchema
import androidx.wear.watchface.style.UserStyleSetting
import androidx.wear.watchface.style.WatchFaceLayer
@@ -261,6 +263,74 @@ internal class TestWatchfaceOverlayStyleWatchFaceService(
.setOverlayStyle(watchFaceOverlayStyle)
}
+@Suppress("Deprecation")
+internal class TestWatchFaceRuntimeService(
+ testContext: Context,
+ private var surfaceHolderOverride: SurfaceHolder
+) : WatchFaceRuntimeService() {
+
+ lateinit var lastResourceOnlyWatchFacePackageName: String
+
+ init {
+ attachBaseContext(testContext)
+ }
+
+ override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+ override fun createUserStyleSchema(resourceOnlyWatchFacePackageName: String) =
+ UserStyleSchema(emptyList())
+
+ override fun createComplicationSlotsManager(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String
+ ) = ComplicationSlotsManager(emptyList(), currentUserStyleRepository)
+
+ override fun createUserStyleFlavors(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ complicationSlotsManager: ComplicationSlotsManager,
+ resourceOnlyWatchFacePackageName: String
+ ) = UserStyleFlavors()
+
+ override suspend fun createWatchFace(
+ surfaceHolder: SurfaceHolder,
+ watchState: WatchState,
+ complicationSlotsManager: ComplicationSlotsManager,
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String
+ ): WatchFace {
+ lastResourceOnlyWatchFacePackageName = resourceOnlyWatchFacePackageName
+
+ return WatchFace(
+ WatchFaceType.DIGITAL,
+ @Suppress("deprecation")
+ object :
+ Renderer.CanvasRenderer(
+ surfaceHolder,
+ currentUserStyleRepository,
+ watchState,
+ CanvasType.HARDWARE,
+ 16
+ ) {
+ override fun render(
+ canvas: Canvas,
+ bounds: Rect,
+ zonedDateTime: ZonedDateTime
+ ) {
+ // Actually rendering something isn't required.
+ }
+
+ override fun renderHighlightLayer(
+ canvas: Canvas,
+ bounds: Rect,
+ zonedDateTime: ZonedDateTime
+ ) {
+ // Actually rendering something isn't required.
+ }
+ }
+ )
+ }
+}
+
internal class TestAsyncCanvasRenderInitWatchFaceService(
testContext: Context,
private var surfaceHolderOverride: SurfaceHolder,
diff --git a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
index 6e145da87ea..1769ce1f735 100644
--- a/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
+++ b/wear/watchface/watchface-client/src/androidTest/java/androidx/wear/watchface/client/test/WatchFaceControlClientTest.kt
@@ -133,7 +133,8 @@ abstract class WatchFaceControlClientTestBase {
context,
Intent(context, WatchFaceControlTestService::class.java).apply {
action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
- }
+ },
+ resourceOnlyWatchFacePackageName = null
)
}
@@ -503,6 +504,44 @@ class WatchFaceControlClientTest : WatchFaceControlClientTestBase() {
}
@Test
+ @Suppress("deprecation") // getOrCreateInteractiveWatchFaceClient
+ fun resourceOnlyWatchFacePackageName() {
+ val watchFaceService = TestWatchFaceRuntimeService(context, surfaceHolder)
+ val service = runBlocking {
+ WatchFaceControlClient.createWatchFaceControlClientImpl(
+ context,
+ Intent(context, WatchFaceControlTestService::class.java).apply {
+ action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
+ },
+ resourceOnlyWatchFacePackageName = "com.example.watchface"
+ )
+ }
+
+ val deferredInteractiveInstance = handlerCoroutineScope.async {
+ service.getOrCreateInteractiveWatchFaceClient(
+ "testId",
+ deviceConfig,
+ systemState,
+ userStyle = null,
+ complications
+ )
+ }
+
+ // Create the engine which triggers construction of the interactive instance.
+ handler.post {
+ engine = watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper
+ }
+
+ // Wait for the instance to be created.
+ val interactiveInstance = awaitWithTimeout(deferredInteractiveInstance)
+
+ assertThat(watchFaceService.lastResourceOnlyWatchFacePackageName)
+ .isEqualTo("com.example.watchface")
+
+ interactiveInstance.close()
+ }
+
+ @Test
@Suppress("Deprecation")
fun getInteractiveWatchFaceInstance() {
val testId = "testId"
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
index 4414c284de5..833269ba559 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceControlClient.kt
@@ -27,6 +27,7 @@ import androidx.annotation.Px
import androidx.annotation.RestrictTo
import androidx.core.util.Consumer
import androidx.wear.watchface.Renderer
+import androidx.wear.watchface.WatchFaceService
import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
import androidx.wear.watchface.complications.data.ComplicationData
import androidx.wear.watchface.complications.data.ComplicationType
@@ -79,13 +80,52 @@ public interface WatchFaceControlClient : AutoCloseable {
context,
Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
setPackage(watchFacePackageName)
- }
+ },
+ null
+ )
+
+ /**
+ * Similar [createWatchFaceControlClient] this constructs a [WatchFaceControlClient] which
+ * attempts to connect to the watch face runtime in the android package
+ * [runtimePackageName].
+ *
+ * A watch face runtime is a special type of watch face, which renders a watch face
+ * described by resources in another package [resourceOnlyWatchFacePackageName].
+ *
+ * Currently Wear OS only supports the runtime for the Android Watch Face Format (see
+ * https://developer.android.com/training/wearables/wff for more details).
+ *
+ * @param context Calling application's [Context].
+ * @param runtimePackageName The name of the package containing the watch face runtime's
+ * control service to bind to.
+ * @param resourceOnlyWatchFacePackageName The name of the package from which to load the
+ * resource only watch face. This is exposed to the runtime via
+ * [WatchFaceService.resourceOnlyWatchFacePackageName]. Note only one watch face
+ * definition per resource only watch face package is supported.
+ * @return The [WatchFaceControlClient] if there is one.
+ * @throws [ServiceNotBoundException] if the watch face control service can not be bound or
+ * a [ServiceStartFailureException] if the watch face dies during startup.
+ */
+ @JvmStatic
+ @Throws(ServiceNotBoundException::class, ServiceStartFailureException::class)
+ public suspend fun createWatchFaceRuntimeControlClient(
+ context: Context,
+ runtimePackageName: String,
+ resourceOnlyWatchFacePackageName: String
+ ): WatchFaceControlClient =
+ createWatchFaceControlClientImpl(
+ context,
+ Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
+ setPackage(runtimePackageName)
+ },
+ resourceOnlyWatchFacePackageName
)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public suspend fun createWatchFaceControlClientImpl(
context: Context,
- intent: Intent
+ intent: Intent,
+ resourceOnlyWatchFacePackageName: String?
): WatchFaceControlClient {
val deferredService = CompletableDeferred<IWatchFaceControlService>()
val traceEvent = AsyncTraceEvent("WatchFaceControlClientImpl.bindService")
@@ -107,7 +147,12 @@ public interface WatchFaceControlClient : AutoCloseable {
traceEvent.close()
throw ServiceNotBoundException()
}
- return WatchFaceControlClientImpl(context, deferredService.await(), serviceConnection)
+ return WatchFaceControlClientImpl(
+ context,
+ deferredService.await(),
+ serviceConnection,
+ resourceOnlyWatchFacePackageName
+ )
}
}
@@ -342,7 +387,8 @@ internal class WatchFaceControlClientImpl
internal constructor(
private val context: Context,
private val service: IWatchFaceControlService,
- private val serviceConnection: ServiceConnection
+ private val serviceConnection: ServiceConnection,
+ private val resourceOnlyWatchFacePackageName: String?
) : WatchFaceControlClient {
private var closed = false
@@ -499,8 +545,8 @@ internal constructor(
it.value.asWireComplicationData()
)
},
- null,
- null
+ /* auxiliaryComponentPackageName = */ resourceOnlyWatchFacePackageName,
+ /* auxiliaryComponentClassName = */ null
),
object : IPendingInteractiveWatchFace.Stub() {
override fun getApiVersion() =
diff --git a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
index f1569cb54e7..a0dfe0024b8 100644
--- a/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
+++ b/wear/watchface/watchface-client/src/main/java/androidx/wear/watchface/client/WatchFaceMetadataClient.kt
@@ -93,6 +93,43 @@ public interface WatchFaceMetadataClient : AutoCloseable {
)
}
+ /**
+ * Constructs a [WatchFaceMetadataClient] for fetching metadata for the specified resource
+ * only watch face from its runtime. A resource only watch face runtime is a special watch
+ * face that knows how to load watch faces from resources in another package that contains
+ * only resources and no executable code.
+ *
+ * @param context Calling application's [Context].
+ * @param watchFaceName The [ComponentName] of the watch face to fetch meta data from.
+ * @param runtimePackage The package that contains the Resource only Watch Face runtime.
+ * @return The [WatchFaceMetadataClient] if there is one.
+ * @throws [ServiceNotBoundException] if the underlying watch face control service can not
+ * be bound or a [ServiceStartFailureException] if the watch face dies during startup. If
+ * the service's manifest contains an
+ * androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data node then
+ * [PackageManager.NameNotFoundException] is thrown if [watchFaceName] is invalid.
+ */
+ @Throws(
+ ServiceNotBoundException::class,
+ ServiceStartFailureException::class,
+ PackageManager.NameNotFoundException::class
+ )
+ @SuppressWarnings("MissingJvmstatic") // Can't really call a suspend fun from java.
+ public suspend fun createForRuntime(
+ context: Context,
+ watchFaceName: ComponentName,
+ runtimePackage: String
+ ): WatchFaceMetadataClient {
+ return createImpl(
+ context,
+ Intent(WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE).apply {
+ setPackage(runtimePackage)
+ },
+ watchFaceName,
+ parserProvider = null
+ )
+ }
+
@Suppress("DEPRECATION") // getServiceInfo
internal fun isXmlVersionCompatible(
context: Context,
@@ -156,10 +193,10 @@ public interface WatchFaceMetadataClient : AutoCloseable {
context: Context,
intent: Intent,
watchFaceName: ComponentName,
- parserProvider: ParserProvider
+ parserProvider: ParserProvider?
): WatchFaceMetadataClient {
// Check if there's static metadata we can read (fast).
- parserProvider.getParser(context, watchFaceName)?.let {
+ parserProvider?.getParser(context, watchFaceName)?.let {
return XmlWatchFaceMetadataClientImpl(
XmlSchemaAndComplicationSlotsDefinition.inflate(
context.packageManager.getResourcesForApplication(
diff --git a/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/control/data/WallpaperInteractiveWatchFaceInstanceParams.java b/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/control/data/WallpaperInteractiveWatchFaceInstanceParams.java
index 5e20ee57fa0..f9ea844685b 100644
--- a/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/control/data/WallpaperInteractiveWatchFaceInstanceParams.java
+++ b/wear/watchface/watchface-data/src/main/java/androidx/wear/watchface/control/data/WallpaperInteractiveWatchFaceInstanceParams.java
@@ -145,6 +145,16 @@ public class WallpaperInteractiveWatchFaceInstanceParams
mIdAndComplicationDataWireFormats = idAndComplicationDataWireFormats;
}
+ @Nullable
+ public String getAuxiliaryComponentClassName() {
+ return mAuxiliaryComponentClassName;
+ }
+
+ @Nullable
+ public String getAuxiliaryComponentPackageName() {
+ return mAuxiliaryComponentPackageName;
+ }
+
/**
* Serializes this WallpaperInteractiveWatchFaceInstanceParams to the specified {@link Parcel}.
*/
diff --git a/wear/watchface/watchface-guava/api/current.txt b/wear/watchface/watchface-guava/api/current.txt
index 2460e8b2b2d..115e276a5ec 100644
--- a/wear/watchface/watchface-guava/api/current.txt
+++ b/wear/watchface/watchface-guava/api/current.txt
@@ -43,6 +43,12 @@ package androidx.wear.watchface {
method @UiThread public final void runUiThreadGlCommands(Runnable runnable);
}
+ public abstract class ListenableWatchFaceRuntimeService extends androidx.wear.watchface.WatchFaceRuntimeService {
+ ctor public ListenableWatchFaceRuntimeService();
+ method protected final suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+ method protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.WatchFace> createWatchFaceFutureAsync(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName);
+ }
+
public abstract class ListenableWatchFaceService extends androidx.wear.watchface.WatchFaceService {
ctor public ListenableWatchFaceService();
method protected suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
diff --git a/wear/watchface/watchface-guava/api/restricted_current.txt b/wear/watchface/watchface-guava/api/restricted_current.txt
index 2460e8b2b2d..115e276a5ec 100644
--- a/wear/watchface/watchface-guava/api/restricted_current.txt
+++ b/wear/watchface/watchface-guava/api/restricted_current.txt
@@ -43,6 +43,12 @@ package androidx.wear.watchface {
method @UiThread public final void runUiThreadGlCommands(Runnable runnable);
}
+ public abstract class ListenableWatchFaceRuntimeService extends androidx.wear.watchface.WatchFaceRuntimeService {
+ ctor public ListenableWatchFaceRuntimeService();
+ method protected final suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+ method protected abstract com.google.common.util.concurrent.ListenableFuture<androidx.wear.watchface.WatchFace> createWatchFaceFutureAsync(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName);
+ }
+
public abstract class ListenableWatchFaceService extends androidx.wear.watchface.WatchFaceService {
ctor public ListenableWatchFaceService();
method protected suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRenderer2Test.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRenderer2Test.kt
index c831044466e..540297d5f5f 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRenderer2Test.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRenderer2Test.kt
@@ -137,11 +137,12 @@ public class AsyncListenableCanvasRenderer2Test : WatchFaceControlClientServiceT
initFuture,
sharedAssetsFuture
)
+ val controlClient = createWatchFaceControlClientService()
val deferredClient =
handlerCoroutineScope.async {
@Suppress("deprecation")
- watchFaceControlClientService.getOrCreateInteractiveWatchFaceClient(
+ controlClient.getOrCreateInteractiveWatchFaceClient(
"testId",
DeviceConfig(false, false, 0, 0),
WatchUiState(false, 0),
@@ -150,7 +151,7 @@ public class AsyncListenableCanvasRenderer2Test : WatchFaceControlClientServiceT
)
}
- handler.post { watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper }
+ handler.post { watchFaceService.onCreateEngine() }
val client = awaitWithTimeout(deferredClient)
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRendererTest.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRendererTest.kt
index 66ce0b78326..491c44373d2 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRendererTest.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableCanvasRendererTest.kt
@@ -109,11 +109,12 @@ public class AsyncListenableCanvasRendererTest : WatchFaceControlClientServiceTe
val initFuture = SettableFuture.create<Unit>()
val watchFaceService =
TestAsyncCanvasRenderInitWatchFaceService(context, surfaceHolder, initFuture)
+ val controlClient = createWatchFaceControlClientService()
val deferredClient =
handlerCoroutineScope.async {
@Suppress("deprecation")
- watchFaceControlClientService.getOrCreateInteractiveWatchFaceClient(
+ controlClient.getOrCreateInteractiveWatchFaceClient(
"testId",
DeviceConfig(false, false, 0, 0),
WatchUiState(false, 0),
@@ -122,7 +123,7 @@ public class AsyncListenableCanvasRendererTest : WatchFaceControlClientServiceTe
)
}
- handler.post { watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper }
+ handler.post { watchFaceService.onCreateEngine() }
val client = awaitWithTimeout(deferredClient)
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRenderer2Test.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRenderer2Test.kt
index b7edca4ceee..e64777b1817 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRenderer2Test.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRenderer2Test.kt
@@ -127,11 +127,12 @@ public class AsyncListenableGlesRenderer2Test : WatchFaceControlClientServiceTes
onBackgroundThreadGlContextFuture,
sharedAssetsFuture
)
+ val controlClient = createWatchFaceControlClientService()
val deferredClient =
handlerCoroutineScope.async {
@Suppress("deprecation")
- watchFaceControlClientService.getOrCreateInteractiveWatchFaceClient(
+ controlClient.getOrCreateInteractiveWatchFaceClient(
"testId",
DeviceConfig(false, false, 0, 0),
WatchUiState(false, 0),
@@ -140,7 +141,7 @@ public class AsyncListenableGlesRenderer2Test : WatchFaceControlClientServiceTes
)
}
- handler.post { watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper }
+ handler.post { watchFaceService.onCreateEngine() }
val client = awaitWithTimeout(deferredClient)
try {
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRendererTest.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRendererTest.kt
index 21de4366fea..0c292bc3b8e 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRendererTest.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableGlesRendererTest.kt
@@ -106,11 +106,12 @@ public class AsyncListenableGlesRendererTest : WatchFaceControlClientServiceTest
onUiThreadGlSurfaceCreatedFuture,
onBackgroundThreadGlContextFuture
)
+ val controlClient = createWatchFaceControlClientService()
val deferredClient =
handlerCoroutineScope.async {
@Suppress("deprecation")
- watchFaceControlClientService.getOrCreateInteractiveWatchFaceClient(
+ controlClient.getOrCreateInteractiveWatchFaceClient(
"testId",
DeviceConfig(false, false, 0, 0),
WatchUiState(false, 0),
@@ -119,7 +120,7 @@ public class AsyncListenableGlesRendererTest : WatchFaceControlClientServiceTest
)
}
- handler.post { watchFaceService.onCreateEngine() as WatchFaceService.EngineWrapper }
+ handler.post { watchFaceService.onCreateEngine() }
val client = awaitWithTimeout(deferredClient)
try {
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableWatchFaceServiceTest.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableWatchFaceServiceTest.kt
index c4a385b9ce1..b082a313ff4 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableWatchFaceServiceTest.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/AsyncListenableWatchFaceServiceTest.kt
@@ -16,12 +16,18 @@
package androidx.wear.watchface
+import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
+import android.os.Build
import android.view.SurfaceHolder
+import androidx.annotation.RequiresApi
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
+import androidx.wear.watchface.client.DeviceConfig
+import androidx.wear.watchface.client.WatchUiState
import androidx.wear.watchface.style.CurrentUserStyleRepository
+import androidx.wear.watchface.style.UserStyleFlavors
import androidx.wear.watchface.style.UserStyleSchema
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.ListenableFuture
@@ -30,6 +36,7 @@ import java.time.Instant
import java.time.ZonedDateTime
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.async
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
@@ -90,6 +97,56 @@ private class TestAsyncListenableWatchFaceService : ListenableWatchFaceService()
)
}
+private class TestAsyncListenableWatchFaceRuntimeService(
+ testContext: Context,
+ private var surfaceHolderOverride: SurfaceHolder,
+) : ListenableWatchFaceRuntimeService() {
+ lateinit var lastResourceOnlyWatchFacePackageName: String
+
+ init {
+ attachBaseContext(testContext)
+ }
+
+ override fun getWallpaperSurfaceHolderOverride() = surfaceHolderOverride
+
+ override fun createWatchFaceFutureAsync(
+ surfaceHolder: SurfaceHolder,
+ watchState: WatchState,
+ complicationSlotsManager: ComplicationSlotsManager,
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String
+ ): ListenableFuture<WatchFace> {
+ lastResourceOnlyWatchFacePackageName = resourceOnlyWatchFacePackageName
+
+ val future = SettableFuture.create<WatchFace>()
+ // Post a task to resolve the future.
+ getUiThreadHandler().post {
+ future.set(
+ WatchFace(
+ WatchFaceType.DIGITAL,
+ FakeRenderer(surfaceHolder, watchState, currentUserStyleRepository)
+ )
+ .apply { setOverridePreviewReferenceInstant(REFERENCE_PREVIEW_TIME) }
+ )
+ }
+ return future
+ }
+
+ override fun createUserStyleSchema(resourceOnlyWatchFacePackageName: String) =
+ UserStyleSchema(emptyList())
+
+ override fun createComplicationSlotsManager(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String
+ ) = ComplicationSlotsManager(emptyList(), currentUserStyleRepository)
+
+ override fun createUserStyleFlavors(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ complicationSlotsManager: ComplicationSlotsManager,
+ resourceOnlyWatchFacePackageName: String
+ ) = UserStyleFlavors()
+}
+
/**
* Illustrates that createWatchFaceFuture can be resolved in a different task posted to the main
* looper.
@@ -126,3 +183,39 @@ public class AsyncListenableWatchFaceServiceTest {
assertThat(watchFace.overridePreviewReferenceInstant).isEqualTo(REFERENCE_PREVIEW_TIME)
}
}
+
+@RunWith(AndroidJUnit4::class)
+@RequiresApi(Build.VERSION_CODES.O_MR1)
+@MediumTest
+public class AsyncListenableWatchFaceRuntimeServiceTest : WatchFaceControlClientServiceTest() {
+
+ @Test
+ public fun asyncTest() {
+ val service = TestAsyncListenableWatchFaceRuntimeService(context, surfaceHolder)
+ val controlClient = createWatchFaceRuntimeControlClientService("com.example.wf")
+
+ val deferredClient =
+ handlerCoroutineScope.async {
+ @Suppress("deprecation")
+ controlClient.getOrCreateInteractiveWatchFaceClient(
+ "testId",
+ DeviceConfig(false, false, 0, 0),
+ WatchUiState(false, 0),
+ null,
+ emptyMap()
+ )
+ }
+
+ handler.post { service.onCreateEngine() }
+
+ val client = awaitWithTimeout(deferredClient)
+
+ // To avoid a race condition, we need to wait for the watchface to be fully created, which
+ // this does.
+ client.complicationSlotsState
+
+ assertThat(service.lastResourceOnlyWatchFacePackageName).isEqualTo("com.example.wf")
+
+ client.close()
+ }
+} \ No newline at end of file
diff --git a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/WatchFaceControlTestService.kt b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/WatchFaceControlTestService.kt
index 9ebc13c1150..029435fcaa9 100644
--- a/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/WatchFaceControlTestService.kt
+++ b/wear/watchface/watchface-guava/src/androidTest/java/androidx/wear/watchface/WatchFaceControlTestService.kt
@@ -91,14 +91,27 @@ public open class WatchFaceControlClientServiceTest {
val glSurface = Surface(surfaceTexture)
val glSurfaceHolder = Mockito.mock(SurfaceHolder::class.java)
- val watchFaceControlClientService = runBlocking {
- WatchFaceControlClient.createWatchFaceControlClientImpl(
- context,
- Intent(context, WatchFaceControlTestService::class.java).apply {
- action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
- }
- )
- }
+ fun createWatchFaceControlClientService() =
+ runBlocking {
+ WatchFaceControlClient.createWatchFaceControlClientImpl(
+ context,
+ Intent(context, WatchFaceControlTestService::class.java).apply {
+ action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
+ },
+ resourceOnlyWatchFacePackageName = null
+ )
+ }
+
+ fun createWatchFaceRuntimeControlClientService(resourceOnlyWatchFacePackageName: String) =
+ runBlocking {
+ WatchFaceControlClient.createWatchFaceControlClientImpl(
+ context,
+ Intent(context, WatchFaceControlTestService::class.java).apply {
+ action = WatchFaceControlService.ACTION_WATCHFACE_CONTROL_SERVICE
+ },
+ resourceOnlyWatchFacePackageName
+ )
+ }
@Before
fun setUp() {
diff --git a/wear/watchface/watchface-guava/src/main/java/androidx/wear/watchface/ListenableWatchFaceService.kt b/wear/watchface/watchface-guava/src/main/java/androidx/wear/watchface/ListenableWatchFaceService.kt
index a1e25de8382..28542f469db 100644
--- a/wear/watchface/watchface-guava/src/main/java/androidx/wear/watchface/ListenableWatchFaceService.kt
+++ b/wear/watchface/watchface-guava/src/main/java/androidx/wear/watchface/ListenableWatchFaceService.kt
@@ -73,3 +73,58 @@ public abstract class ListenableWatchFaceService : WatchFaceService() {
future.addListener({ it.resume(future.get()) }, { runnable -> runnable.run() })
}
}
+
+/**
+ * [ListenableFuture]-based compatibility wrapper around [WatchFaceRuntimeService]'s suspending
+ * [WatchFaceService.createWatchFace].
+ */
+public abstract class ListenableWatchFaceRuntimeService : WatchFaceRuntimeService() {
+ /**
+ * Override this factory method to create your WatchFaceImpl. This method will be called by the
+ * library on a background thread, if possible any expensive initialization should be done
+ * asynchronously. The [WatchFace] and its [Renderer] should be accessed exclusively from the
+ * UiThread afterwards. There is a memory barrier between construction and rendering so no
+ * special threading primitives are required.
+ *
+ * Warning the system will likely time out waiting for watch face initialization if it takes
+ * longer than [MAX_CREATE_WATCHFACE_TIME_MILLIS] milliseconds.
+ *
+ * Note cancellation of the returned future is not supported.
+ *
+ * @param surfaceHolder The [SurfaceHolder] to pass to the [Renderer]'s constructor.
+ * @param watchState The [WatchState] for the watch face.
+ * @param complicationSlotsManager The [ComplicationSlotsManager] returned by
+ * [createComplicationSlotsManager].
+ * @param currentUserStyleRepository The [CurrentUserStyleRepository] constructed using the
+ * [UserStyleSchema] returned by [createUserStyleSchema].
+ * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+ * definition should be loaded.
+ * @return A [ListenableFuture] for a [WatchFace] whose [Renderer] uses the provided
+ * [surfaceHolder].
+ */
+ protected abstract fun createWatchFaceFutureAsync(
+ surfaceHolder: SurfaceHolder,
+ watchState: WatchState,
+ complicationSlotsManager: ComplicationSlotsManager,
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String
+ ): ListenableFuture<WatchFace>
+
+ final override suspend fun createWatchFace(
+ surfaceHolder: SurfaceHolder,
+ watchState: WatchState,
+ complicationSlotsManager: ComplicationSlotsManager,
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String
+ ): WatchFace = suspendCancellableCoroutine {
+ val future =
+ createWatchFaceFutureAsync(
+ surfaceHolder,
+ watchState,
+ complicationSlotsManager,
+ currentUserStyleRepository,
+ resourceOnlyWatchFacePackageName
+ )
+ future.addListener({ it.resume(future.get()) }, { runnable -> runnable.run() })
+ }
+} \ No newline at end of file
diff --git a/wear/watchface/watchface/api/current.txt b/wear/watchface/watchface/api/current.txt
index cc6e5d8a926..ba6196763db 100644
--- a/wear/watchface/watchface/api/current.txt
+++ b/wear/watchface/watchface/api/current.txt
@@ -394,6 +394,18 @@ package androidx.wear.watchface {
@SuppressCompatibility @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface WatchFaceExperimental {
}
+ public abstract class WatchFaceRuntimeService extends androidx.wear.watchface.WatchFaceService {
+ ctor public WatchFaceRuntimeService();
+ method protected final androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
+ method @WorkerThread protected abstract androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName);
+ method protected final androidx.wear.watchface.style.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager);
+ method @WorkerThread protected abstract androidx.wear.watchface.style.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, String resourceOnlyWatchFacePackageName);
+ method protected final androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
+ method @WorkerThread protected abstract androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema(String resourceOnlyWatchFacePackageName);
+ method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+ method protected final suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+ }
+
public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
ctor public WatchFaceService();
method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
diff --git a/wear/watchface/watchface/api/restricted_current.txt b/wear/watchface/watchface/api/restricted_current.txt
index cc6e5d8a926..ba6196763db 100644
--- a/wear/watchface/watchface/api/restricted_current.txt
+++ b/wear/watchface/watchface/api/restricted_current.txt
@@ -394,6 +394,18 @@ package androidx.wear.watchface {
@SuppressCompatibility @kotlin.RequiresOptIn(message="This is an experimental API that may change or be removed without warning.") @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) public @interface WatchFaceExperimental {
}
+ public abstract class WatchFaceRuntimeService extends androidx.wear.watchface.WatchFaceService {
+ ctor public WatchFaceRuntimeService();
+ method protected final androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
+ method @WorkerThread protected abstract androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName);
+ method protected final androidx.wear.watchface.style.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager);
+ method @WorkerThread protected abstract androidx.wear.watchface.style.UserStyleFlavors createUserStyleFlavors(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, String resourceOnlyWatchFacePackageName);
+ method protected final androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema();
+ method @WorkerThread protected abstract androidx.wear.watchface.style.UserStyleSchema createUserStyleSchema(String resourceOnlyWatchFacePackageName);
+ method @WorkerThread protected abstract suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, String resourceOnlyWatchFacePackageName, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+ method protected final suspend Object? createWatchFace(android.view.SurfaceHolder surfaceHolder, androidx.wear.watchface.WatchState watchState, androidx.wear.watchface.ComplicationSlotsManager complicationSlotsManager, androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository, kotlin.coroutines.Continuation<? super androidx.wear.watchface.WatchFace>);
+ }
+
public abstract class WatchFaceService extends android.service.wallpaper.WallpaperService {
ctor public WatchFaceService();
method @WorkerThread protected androidx.wear.watchface.ComplicationSlotsManager createComplicationSlotsManager(androidx.wear.watchface.style.CurrentUserStyleRepository currentUserStyleRepository);
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
index 20f94603963..9d57c46fa01 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFace.kt
@@ -249,7 +249,8 @@ public class WatchFace(
}
.apply { setContext(context) }
- val engine = watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ val engine = watchFaceService.createHeadlessEngine(componentName)
+ as WatchFaceService.EngineWrapper
val headlessWatchFaceImpl = engine.createHeadlessInstance(params)
return engine.deferredWatchFaceImpl.await().WFEditorDelegate(headlessWatchFaceImpl)
}
@@ -665,11 +666,7 @@ constructor(
internal var lastDrawTimeMillis: Long = 0
internal var nextDrawTimeMillis: Long = 0
- internal val componentName =
- ComponentName(
- watchFaceHostApi.getContext().packageName,
- watchFaceHostApi.getContext().javaClass.name
- )
+ internal val componentName = watchFaceHostApi.getComponentName()
internal fun getWatchFaceStyle() =
WatchFaceStyle(
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
index 3f160d078b1..4d6b2dce56c 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceHostApi.kt
@@ -149,4 +149,7 @@ public interface WatchFaceHostApi {
/** Requests the system to capture an updated preview image. */
public fun sendPreviewImageNeedsUpdateRequest() {}
+
+ /** Returns ComponentName of the watch face. */
+ public fun getComponentName(): ComponentName
}
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index d9c04a97b2d..9f2b9f2c003 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -299,6 +299,12 @@ public abstract class WatchFaceService : WallpaperService() {
public companion object {
private const val TAG = "WatchFaceService"
+ /**
+ * [ComponentName] is not allowed to have null for the class name (and it can be null for
+ * resource only watch faces), hence this placeholder.
+ */
+ private const val RESOURCE_ONLY_CLASS_NAME_PLACEHOLDER = "null"
+
/** Whether to log every frame. */
private const val LOG_VERBOSE = false
@@ -446,7 +452,7 @@ public abstract class WatchFaceService : WallpaperService() {
}
}
- private val xmlSchemaAndComplicationSlotsDefinition:
+ internal val xmlSchemaAndComplicationSlotsDefinition:
XmlSchemaAndComplicationSlotsDefinition by lazy {
val resourceId = getXmlWatchFaceResourceId()
if (resourceId == 0) {
@@ -477,6 +483,10 @@ public abstract class WatchFaceService : WallpaperService() {
xmlSchemaAndComplicationSlotsDefinition.schema?.userStyleSettings ?: emptyList()
)
+ internal open fun createUserStyleSchemaInternal(
+ resourceOnlyWatchFacePackageName: String?
+ ): UserStyleSchema = createUserStyleSchema()
+
/**
* If the WatchFaceService's manifest doesn't define a
* androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data tag then override
@@ -501,6 +511,13 @@ public abstract class WatchFaceService : WallpaperService() {
getComplicationSlotInflationFactory(currentUserStyleRepository)
)
+ internal open fun createComplicationSlotsManagerInternal(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String?
+ ): ComplicationSlotsManager = createComplicationSlotsManager(
+ currentUserStyleRepository
+ )
+
/**
* Used when inflating [ComplicationSlot]s from XML to provide a
* [ComplicationSlotInflationFactory] which provides the [CanvasComplicationFactory] and where
@@ -566,6 +583,15 @@ public abstract class WatchFaceService : WallpaperService() {
complicationSlotsManager: ComplicationSlotsManager
): UserStyleFlavors = xmlSchemaAndComplicationSlotsDefinition.flavors ?: UserStyleFlavors()
+ internal open fun createUserStyleFlavorsInternal(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ complicationSlotsManager: ComplicationSlotsManager,
+ resourceOnlyWatchFacePackageName: String?
+ ): UserStyleFlavors = createUserStyleFlavors(
+ currentUserStyleRepository,
+ complicationSlotsManager
+ )
+
/**
* Override this factory method to create your WatchFaceImpl. This method will be called by the
* library on a background thread, if possible any expensive initialization should be done
@@ -592,13 +618,26 @@ public abstract class WatchFaceService : WallpaperService() {
currentUserStyleRepository: CurrentUserStyleRepository
): WatchFace
+ internal open suspend fun createWatchFaceInternal(
+ surfaceHolder: SurfaceHolder,
+ watchState: WatchState,
+ complicationSlotsManager: ComplicationSlotsManager,
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String?
+ ): WatchFace = createWatchFace(
+ surfaceHolder,
+ watchState,
+ complicationSlotsManager,
+ currentUserStyleRepository,
+ )
+
/** Creates an interactive engine for WallpaperService. */
final override fun onCreateEngine(): Engine =
- EngineWrapper(getUiThreadHandler(), getBackgroundThreadHandler(), false)
+ EngineWrapper(getUiThreadHandler(), getBackgroundThreadHandler(), null)
/** Creates a headless engine. */
- internal fun createHeadlessEngine(): Engine =
- EngineWrapper(getUiThreadHandler(), getBackgroundThreadHandler(), true)
+ internal fun createHeadlessEngine(watchFaceName: ComponentName): Engine =
+ EngineWrapper(getUiThreadHandler(), getBackgroundThreadHandler(), watchFaceName)
/** Returns the ui thread [Handler]. */
public fun getUiThreadHandler(): Handler = getUiThreadHandlerImpl()
@@ -1149,7 +1188,7 @@ public abstract class WatchFaceService : WallpaperService() {
public inner class EngineWrapper(
private val uiThreadHandler: Handler,
private val backgroundThreadHandler: Handler,
- headless: Boolean
+ headlessComponentName: ComponentName?
) :
WallpaperService.Engine(),
WatchFaceHostApi,
@@ -1197,7 +1236,7 @@ public abstract class WatchFaceService : WallpaperService() {
// That's supposed to get sent very quickly, but in case it doesn't we initially
// assume we're not in ambient mode which should be correct most of the time.
isAmbient.value = false
- isHeadless = headless
+ isHeadless = (headlessComponentName != null)
isLocked.value =
(getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager).isDeviceLocked
}
@@ -1277,6 +1316,7 @@ public abstract class WatchFaceService : WallpaperService() {
internal var immutableSystemStateDone = false
internal var immutableChinHeightDone = false
internal var systemHasSentWatchUiState = false
+ internal var resourceOnlyWatchFacePackageName: String? = headlessComponentName?.packageName
private var asyncWatchFaceConstructionPending = false
@@ -1885,23 +1925,34 @@ public abstract class WatchFaceService : WallpaperService() {
/** This will be called from a binder thread. */
@WorkerThread
internal fun getDefaultProviderPolicies(): Array<IdTypeAndDefaultProviderPolicyWireFormat> {
- return createComplicationSlotsManager(
- CurrentUserStyleRepository(createUserStyleSchema())
+ return createComplicationSlotsManagerInternal(
+ CurrentUserStyleRepository(
+ createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)
+ ),
+ resourceOnlyWatchFacePackageName
)
.getDefaultProviderPolicies()
}
/** This will be called from a binder thread. */
@WorkerThread
- internal fun getUserStyleSchemaWireFormat() = createUserStyleSchema().toWireFormat()
+ internal fun getUserStyleSchemaWireFormat() =
+ createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName).toWireFormat()
/** This will be called from a binder thread. */
@WorkerThread
internal fun getUserStyleFlavorsWireFormat(): UserStyleFlavorsWireFormat {
- val currentUserStyleRepository = CurrentUserStyleRepository(createUserStyleSchema())
- return createUserStyleFlavors(
+ val currentUserStyleRepository =
+ CurrentUserStyleRepository(
+ createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)
+ )
+ return createUserStyleFlavorsInternal(
currentUserStyleRepository,
- createComplicationSlotsManager(currentUserStyleRepository)
+ createComplicationSlotsManagerInternal(
+ currentUserStyleRepository,
+ resourceOnlyWatchFacePackageName
+ ),
+ resourceOnlyWatchFacePackageName
)
.toWireFormat()
}
@@ -1910,7 +1961,11 @@ public abstract class WatchFaceService : WallpaperService() {
@OptIn(ComplicationExperimental::class)
@WorkerThread
internal fun getComplicationSlotMetadataWireFormats() =
- createComplicationSlotsManager(CurrentUserStyleRepository(createUserStyleSchema()))
+ createComplicationSlotsManagerInternal(
+ CurrentUserStyleRepository(
+ createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)),
+ resourceOnlyWatchFacePackageName
+ )
.complicationSlots
.map {
val systemDataSourceFallbackDefaultType =
@@ -2043,6 +2098,8 @@ public abstract class WatchFaceService : WallpaperService() {
setWatchUiState(params.watchUiState, fromSysUi = false)
initialUserStyle = params.userStyle
+ resourceOnlyWatchFacePackageName = params.auxiliaryComponentPackageName
+
mutableWatchState.watchFaceInstanceId.value = sanitizeWatchFaceId(params.instanceId)
val watchState = mutableWatchState.asWatchState()
@@ -2108,13 +2165,18 @@ public abstract class WatchFaceService : WallpaperService() {
val timeBefore = System.currentTimeMillis()
val currentUserStyleRepository =
TraceEvent("WatchFaceService.createUserStyleSchema").use {
- CurrentUserStyleRepository(createUserStyleSchema())
+ CurrentUserStyleRepository(
+ createUserStyleSchemaInternal(resourceOnlyWatchFacePackageName)
+ )
}
initStyle(currentUserStyleRepository)
val complicationSlotsManager =
TraceEvent("WatchFaceService.createComplicationsManager").use {
- createComplicationSlotsManager(currentUserStyleRepository)
+ createComplicationSlotsManagerInternal(
+ currentUserStyleRepository,
+ resourceOnlyWatchFacePackageName
+ )
}
complicationSlotsManager.watchFaceHostApi = this@EngineWrapper
complicationSlotsManager.watchState = watchState
@@ -2130,7 +2192,11 @@ public abstract class WatchFaceService : WallpaperService() {
val userStyleFlavors =
TraceEvent("WatchFaceService.createUserStyleFlavors").use {
- createUserStyleFlavors(currentUserStyleRepository, complicationSlotsManager)
+ createUserStyleFlavorsInternal(
+ currentUserStyleRepository,
+ complicationSlotsManager,
+ resourceOnlyWatchFacePackageName
+ )
}
deferredEarlyInitDetails.complete(
@@ -2164,13 +2230,13 @@ public abstract class WatchFaceService : WallpaperService() {
TraceEvent("WatchFaceService.createWatchFace").use {
// Note by awaiting deferredSurfaceHolder we ensure onSurfaceChanged has
// been called and we're passing the correct updated surface holder.
- // This is
- // important for GL rendering.
- createWatchFace(
+ // This is important for GL rendering.
+ createWatchFaceInternal(
surfaceHolder,
watchState,
complicationSlotsManager,
- currentUserStyleRepository
+ currentUserStyleRepository,
+ resourceOnlyWatchFacePackageName
)
}
this@EngineWrapper.deferredWatchFace.complete(watchFace)
@@ -2497,6 +2563,14 @@ public abstract class WatchFaceService : WallpaperService() {
}
}
+ override fun getComponentName(): ComponentName {
+ resourceOnlyWatchFacePackageName?.let {
+ return ComponentName(it, RESOURCE_ONLY_CLASS_NAME_PLACEHOLDER)
+ }
+
+ return ComponentName(getContext().packageName, getContext().javaClass.name)
+ }
+
override fun onWatchFaceColorsChanged(watchFaceColors: WatchFaceColors?) {
synchronized(lock) {
lastWatchFaceColors = watchFaceColors
@@ -2777,6 +2851,7 @@ public abstract class WatchFaceService : WallpaperService() {
writer.println("surfaceDestroyed=$surfaceDestroyed")
writer.println("lastComplications=${complicationsFlow.value.joinToString()}")
writer.println("pendingUpdateTime=${pendingUpdateTime.isPending()}")
+ writer.println("Resource only package name $resourceOnlyWatchFacePackageName")
synchronized(lock) {
forEachListener("dump") { writer.println("listener = ${it.asBinder()}") }
@@ -2824,6 +2899,183 @@ public abstract class WatchFaceService : WallpaperService() {
}
/**
+ * WatchFaceRuntimeService is a special kind of [WatchFaceService], which loads the watch face
+ * definition from another resource only watch face package (see the
+ * `resourceOnlyWatchFacePackageName` parameter passed to [createUserStyleSchema],
+ * [createComplicationSlotsManager], [createUserStyleFlavors] and
+ * [createWatchFace]).
+ *
+ * Note because a WatchFaceRuntimeService loads it's resources from another package, it will need
+ * the following permission:
+ *
+ * ```
+ * <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
+ * tools:ignore="QueryAllPackagesPermission" />
+ * ```
+ *
+ * Currently Wear OS only supports the runtime for the Android Watch Face Format (see
+ * https://developer.android.com/training/wearables/wff for more details).
+ *
+ * Note only one watch face definition per resource only watch face package is supported.
+ */
+abstract class WatchFaceRuntimeService : WatchFaceService() {
+ /**
+ * If the WatchFaceService's manifest doesn't define a
+ * androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data tag then override
+ * this factory method to create a non-empty [UserStyleSchema]. A [CurrentUserStyleRepository]
+ * constructed with this schema will be passed to [createComplicationSlotsManager],
+ * [createUserStyleFlavors] and [createWatchFace]. This is called on a background thread.
+ *
+ * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+ * definition should be loaded.
+ * @return The [UserStyleSchema] to create a [CurrentUserStyleRepository] with, which is passed
+ * to [createComplicationSlotsManager] and [createWatchFace].
+ */
+ @WorkerThread
+ protected abstract fun createUserStyleSchema(
+ resourceOnlyWatchFacePackageName: String
+ ): UserStyleSchema
+
+ override fun createUserStyleSchemaInternal(
+ resourceOnlyWatchFacePackageName: String?
+ ): UserStyleSchema = createUserStyleSchema(resourceOnlyWatchFacePackageName!!)
+
+ @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
+ final override fun createUserStyleSchema(): UserStyleSchema {
+ throw UnsupportedOperationException("resourceOnlyWatchFacePackageName must be specified")
+ }
+
+ /**
+ * If the WatchFaceService's manifest doesn't define a
+ * androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data tag then override
+ * this factory method to create a non-empty [ComplicationSlotsManager]. This manager will be
+ * passed to [createUserStyleFlavors] and [createWatchFace]. This will be called from a
+ * background thread but the ComplicationSlotsManager should be accessed exclusively from the
+ * UiThread afterwards.
+ *
+ * @param currentUserStyleRepository The [CurrentUserStyleRepository] constructed using the
+ * [UserStyleSchema] returned by [createUserStyleSchema].
+ * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+ * definition should be loaded.
+ * @return The [ComplicationSlotsManager] to pass into [createWatchFace].
+ */
+ @WorkerThread
+ protected abstract fun createComplicationSlotsManager(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String
+ ): ComplicationSlotsManager
+
+ internal override fun createComplicationSlotsManagerInternal(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String?
+ ): ComplicationSlotsManager = createComplicationSlotsManager(
+ currentUserStyleRepository,
+ resourceOnlyWatchFacePackageName!!
+ )
+
+ @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
+ final override fun createComplicationSlotsManager(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ ): ComplicationSlotsManager {
+ throw UnsupportedOperationException("resourceOnlyWatchFacePackageName must be specified")
+ }
+
+ /**
+ * If the WatchFaceService's manifest doesn't define a
+ * androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition meta data tag then override
+ * this factory method to create non-empty [UserStyleFlavors]. This is called on a background
+ * thread. The system reads the flavors once and changes may be ignored until the APK is
+ * updated. Metadata tag "androidx.wear.watchface.FLAVORS_SUPPORTED" should be added to let the
+ * system know the service supports flavors.
+ *
+ * @param currentUserStyleRepository The [CurrentUserStyleRepository] constructed using the
+ * [UserStyleSchema] returned by [createUserStyleSchema].
+ * @param complicationSlotsManager The [ComplicationSlotsManager] returned by
+ * [createComplicationSlotsManager]
+ * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+ * definition should be loaded.
+ * @return The [UserStyleFlavors], which is exposed to the system.
+ */
+ @WorkerThread
+ protected abstract fun createUserStyleFlavors(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ complicationSlotsManager: ComplicationSlotsManager,
+ resourceOnlyWatchFacePackageName: String
+ ): UserStyleFlavors
+
+ internal override fun createUserStyleFlavorsInternal(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ complicationSlotsManager: ComplicationSlotsManager,
+ resourceOnlyWatchFacePackageName: String?
+ ): UserStyleFlavors = createUserStyleFlavors(
+ currentUserStyleRepository,
+ complicationSlotsManager,
+ resourceOnlyWatchFacePackageName!!
+ )
+
+ @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
+ final override fun createUserStyleFlavors(
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ complicationSlotsManager: ComplicationSlotsManager
+ ): UserStyleFlavors {
+ throw UnsupportedOperationException("resourceOnlyWatchFacePackageName must be specified")
+ }
+
+ /**
+ * Override this factory method to create your WatchFaceImpl. This method will be called by the
+ * library on a background thread, if possible any expensive initialization should be done
+ * asynchronously. The [WatchFace] and its [Renderer] should be accessed exclusively from the
+ * UiThread afterwards. There is a memory barrier between construction and rendering so no
+ * special threading primitives are required.
+ *
+ * Warning the system will likely time out waiting for watch face initialization if it takes
+ * longer than [WatchFaceService.MAX_CREATE_WATCHFACE_TIME_MILLIS] milliseconds.
+ *
+ * @param surfaceHolder The [SurfaceHolder] to pass to the [Renderer]'s constructor.
+ * @param watchState The [WatchState] for the watch face.
+ * @param complicationSlotsManager The [ComplicationSlotsManager] returned by
+ * [createComplicationSlotsManager].
+ * @param currentUserStyleRepository The [CurrentUserStyleRepository] constructed using the
+ * [UserStyleSchema] returned by [createUserStyleSchema].
+ * @param resourceOnlyWatchFacePackageName The android package from which the watch face
+ * definition should be loaded.
+ * @return A [WatchFace] whose [Renderer] uses the provided [surfaceHolder].
+ */
+ @WorkerThread
+ protected abstract suspend fun createWatchFace(
+ surfaceHolder: SurfaceHolder,
+ watchState: WatchState,
+ complicationSlotsManager: ComplicationSlotsManager,
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String
+ ): WatchFace
+
+ internal override suspend fun createWatchFaceInternal(
+ surfaceHolder: SurfaceHolder,
+ watchState: WatchState,
+ complicationSlotsManager: ComplicationSlotsManager,
+ currentUserStyleRepository: CurrentUserStyleRepository,
+ resourceOnlyWatchFacePackageName: String?
+ ): WatchFace = createWatchFace(
+ surfaceHolder,
+ watchState,
+ complicationSlotsManager,
+ currentUserStyleRepository,
+ resourceOnlyWatchFacePackageName!!
+ )
+
+ @Suppress("DocumentExceptions") // NB this method isn't expected to be called from user code.
+ final override suspend fun createWatchFace(
+ surfaceHolder: SurfaceHolder,
+ watchState: WatchState,
+ complicationSlotsManager: ComplicationSlotsManager,
+ currentUserStyleRepository: CurrentUserStyleRepository
+ ): WatchFace {
+ throw UnsupportedOperationException("resourceOnlyWatchFacePackageName must be specified")
+ }
+}
+
+/**
* Runs the supplied task on the handler thread. If we're not on the handler thread a task is
* posted.
*
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
index 07232b010dd..d4ef01346f8 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/InteractiveInstanceManager.kt
@@ -17,6 +17,7 @@
package androidx.wear.watchface.control
import android.annotation.SuppressLint
+import android.util.Log
import androidx.annotation.UiThread
import androidx.annotation.VisibleForTesting
import androidx.wear.watchface.IndentingPrintWriter
@@ -49,6 +50,8 @@ internal class InteractiveInstanceManager {
)
companion object {
+ private const val TAG = "InteractiveInstanceManager"
+
private val instances = HashMap<String, RefCountedInteractiveWatchFaceInstance>()
private val pendingWallpaperInteractiveWatchFaceInstanceLock = Any()
private var pendingWallpaperInteractiveWatchFaceInstance:
@@ -204,6 +207,18 @@ internal class InteractiveInstanceManager {
// system thinks we should have. Note runBlocking is safe here because we never await.
val engine = impl.engine!!
engine.setUserStyle(value.params.userStyle)
+
+ if (engine.resourceOnlyWatchFacePackageName !=
+ value.params.auxiliaryComponentPackageName
+ ) {
+ val message =
+ "Existing instance has the resourceOnlyWatchFacePackageName of " +
+ "${engine.resourceOnlyWatchFacePackageName}, which is different from the " +
+ "argument watchFaceId of ${value.params.auxiliaryComponentPackageName}."
+ Log.e(TAG, message)
+ throw IllegalStateException(message)
+ }
+
return impl
}
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
index 3ca3862d436..98582e5b4e8 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/control/WatchFaceControlService.kt
@@ -193,7 +193,8 @@ public open class IWatchFaceInstanceServiceStub(
}
watchFaceService.onCreate()
val engine =
- watchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ watchFaceService.createHeadlessEngine(watchFaceName)
+ as WatchFaceService.EngineWrapper
ServiceAndEngine(watchFaceService, engine)
} else {
null
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index b953af0736a..d55140dac7b 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -3319,12 +3319,14 @@ public class WatchFaceServiceTest {
choreographer
)
+ val componentName = ComponentName("test.watchface.app", "test.watchface.class")
engineWrapper =
- testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ testWatchFaceService.createHeadlessEngine(componentName)
+ as WatchFaceService.EngineWrapper
engineWrapper.createHeadlessInstance(
HeadlessWatchFaceInstanceParams(
- ComponentName("test.watchface.app", "test.watchface.class"),
+ componentName,
DeviceConfig(false, false, 100, 200),
100,
100,
@@ -3886,7 +3888,9 @@ public class WatchFaceServiceTest {
choreographer
)
- testWatchFaceService.createHeadlessEngine()
+ testWatchFaceService.createHeadlessEngine(
+ ComponentName("test.watchface.app", "test.watchface.class")
+ )
runPostedTasksFor(0)
@@ -4550,12 +4554,14 @@ public class WatchFaceServiceTest {
engineWrapper.onCreate(surfaceHolder)
engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
+ val componentName = ComponentName("test.watchface.app", "test.watchface.class")
val headlessEngineWrapper =
- testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ testWatchFaceService.createHeadlessEngine(componentName)
+ as WatchFaceService.EngineWrapper
headlessEngineWrapper.createHeadlessInstance(
HeadlessWatchFaceInstanceParams(
- ComponentName("test.watchface.app", "test.watchface.class"),
+ componentName,
DeviceConfig(false, false, 100, 200),
100,
100,
@@ -4691,12 +4697,14 @@ public class WatchFaceServiceTest {
engineWrapper.onCreate(surfaceHolder)
engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
+ val componentName = ComponentName("test.watchface.app", "test.watchface.class")
val headlessEngineWrapper =
- testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ testWatchFaceService.createHeadlessEngine(componentName)
+ as WatchFaceService.EngineWrapper
headlessEngineWrapper.createHeadlessInstance(
HeadlessWatchFaceInstanceParams(
- ComponentName("test.watchface.app", "test.watchface.class"),
+ componentName,
DeviceConfig(false, false, 100, 200),
100,
100,
@@ -4823,12 +4831,14 @@ public class WatchFaceServiceTest {
engineWrapper.onCreate(surfaceHolder)
engineWrapper.onSurfaceChanged(surfaceHolder, 0, 100, 100)
+ val componentName = ComponentName("test.watchface.app", "test.watchface.class")
val headlessEngineWrapper =
- testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ testWatchFaceService.createHeadlessEngine(componentName)
+ as WatchFaceService.EngineWrapper
headlessEngineWrapper.createHeadlessInstance(
HeadlessWatchFaceInstanceParams(
- ComponentName("test.watchface.app", "test.watchface.class"),
+ componentName,
DeviceConfig(false, false, 100, 200),
100,
100,
@@ -5119,12 +5129,14 @@ public class WatchFaceServiceTest {
choreographer
)
+ val componentName = ComponentName("test.watchface.app", "test.watchface.class")
engineWrapper =
- testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ testWatchFaceService.createHeadlessEngine(componentName)
+ as WatchFaceService.EngineWrapper
engineWrapper.createHeadlessInstance(
HeadlessWatchFaceInstanceParams(
- ComponentName("test.watchface.app", "test.watchface.class"),
+ componentName,
DeviceConfig(false, false, 100, 200),
100,
100,
@@ -5187,12 +5199,14 @@ public class WatchFaceServiceTest {
choreographer
)
+ val componentName = ComponentName("test.watchface.app", "test.watchface.class")
engineWrapper =
- testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ testWatchFaceService.createHeadlessEngine(componentName)
+ as WatchFaceService.EngineWrapper
engineWrapper.createHeadlessInstance(
HeadlessWatchFaceInstanceParams(
- ComponentName("test.watchface.app", "test.watchface.class"),
+ componentName,
DeviceConfig(false, false, 100, 200),
100,
100,
@@ -6381,12 +6395,14 @@ public class WatchFaceServiceTest {
forceIsVisible = true
)
+ val componentName = ComponentName("test.watchface.app", "test.watchface.class")
engineWrapper =
- testWatchFaceService.createHeadlessEngine() as WatchFaceService.EngineWrapper
+ testWatchFaceService.createHeadlessEngine(componentName)
+ as WatchFaceService.EngineWrapper
engineWrapper.createHeadlessInstance(
HeadlessWatchFaceInstanceParams(
- ComponentName("test.watchface.app", "test.watchface.class"),
+ componentName,
DeviceConfig(false, false, 100, 200),
100,
100,