aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2022-05-10 03:25:31 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2022-05-10 03:25:31 +0000
commit729038cb548b02f3021e0458846205540dd12293 (patch)
tree553f8ff304f32be9390af44be5d1248137fd8d7e
parent925b6f9a6e9d1af588be0cb7e018f3242799c52c (diff)
parente4e6c123a093030a0bfa0d54d9cea34d6ef781f9 (diff)
downloadsupport-729038cb548b02f3021e0458846205540dd12293.tar.gz
Merge "Add a minimal watch face using multiple instances." into androidx-main
-rw-r--r--settings.gradle1
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/build.gradle62
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/AndroidManifest.xml68
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/BaseFutureCallback.java64
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/ConfigActivity.java146
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/FutureCallback.java63
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/WatchFaceRenderer.java305
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/WatchFaceService.java50
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_icon.pngbin0 -> 5081 bytes
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_minimal_icon.pngbin0 -> 4666 bytes
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_seconds_icon.pngbin0 -> 15985 bytes
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/ic_launcher_background.xml78
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/ic_launcher_foreground.xml15
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/preview.pngbin0 -> 9285 bytes
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/tyme_style_bold_icon.xml19
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/layout/config_activity_layout.xml54
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-anydpi/ic_launcher.xml5
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 4509 bytes
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2419 bytes
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 6114 bytes
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 9880 bytes
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 13873 bytes
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/values/dimens.xml5
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/values/schema_ids.xml21
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/values/strings.xml11
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/xml/watch_face.xml2
-rw-r--r--wear/watchface/watchface-samples-minimal-instances/src/main/res/xml/xml_watchface.xml36
27 files changed, 1005 insertions, 0 deletions
diff --git a/settings.gradle b/settings.gradle
index d61abb04644..05830eaf80a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -823,6 +823,7 @@ includeProject(":wear:watchface:watchface-samples", "wear/watchface/watchface/sa
includeProject(":wear:watchface:watchface-samples-app", "wear/watchface/watchface/samples/app", [BuildType.MAIN, BuildType.WEAR])
includeProject(":wear:watchface:watchface-samples-minimal", "wear/watchface/watchface/samples/minimal", [BuildType.MAIN, BuildType.WEAR])
includeProject(":wear:watchface:watchface-samples-minimal-complications", [BuildType.MAIN, BuildType.WEAR])
+includeProject(":wear:watchface:watchface-samples-minimal-instances", [BuildType.MAIN, BuildType.WEAR])
includeProject(":wear:watchface:watchface-samples-minimal-style", [BuildType.MAIN, BuildType.WEAR])
includeProject(":wear:watchface:watchface-style", [BuildType.MAIN, BuildType.WEAR])
includeProject(":webkit:integration-tests:testapp", [BuildType.MAIN])
diff --git a/wear/watchface/watchface-samples-minimal-instances/build.gradle b/wear/watchface/watchface-samples-minimal-instances/build.gradle
new file mode 100644
index 00000000000..29311051c65
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/build.gradle
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import androidx.build.LibraryType
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.application")
+ id("kotlin-android")
+}
+
+dependencies {
+ api("androidx.activity:activity:1.3.1")
+ api(project(":wear:watchface:watchface"))
+ api(project(":wear:watchface:watchface-editor"))
+ api(project(":wear:watchface:watchface-editor-guava"))
+ api(project(":wear:watchface:watchface-guava"))
+ api(project(":wear:wear"))
+ implementation(libs.guavaAndroid)
+ implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.3.1")
+}
+
+androidx {
+ name = "AndroidX Wear Watchface Minimal Style Sample"
+ type = LibraryType.SAMPLES
+ mavenGroup = LibraryGroups.WEAR_WATCHFACE
+ inceptionYear = "2021"
+ description = "Contains the sample code for the Androidx Wear Watchface library"
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 26
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android.txt')
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ namespace "androidx.wear.watchface.samples.minimal.instances"
+}
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/AndroidManifest.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..2b1f8b1411e
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/AndroidManifest.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <uses-feature android:name="android.hardware.type.watch" />
+
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@android:style/Theme.DeviceDefault"
+ android:fullBackupContent="false">
+
+ <activity
+ android:name="androidx.wear.watchface.samples.minimal.instances.ConfigActivity"
+ android:exported="true"
+ android:label="@string/configuration_title">
+ <intent-filter>
+ <action android:name="androidx.wear.watchface.editor.action.WATCH_FACE_EDITOR" />
+
+ <category
+ android:name="com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <service
+ android:name="androidx.wear.watchface.samples.minimal.instances.WatchFaceService"
+ android:directBootAware="true"
+ android:exported="true"
+ android:label="@string/app_name"
+ android:permission="android.permission.BIND_WALLPAPER">
+
+ <intent-filter>
+ <action android:name="android.service.wallpaper.WallpaperService" />
+ <category android:name="com.google.android.wearable.watchface.category.WATCH_FACE" />
+ </intent-filter>
+
+ <meta-data
+ android:name="com.google.android.wearable.watchface.preview"
+ android:resource="@drawable/preview" />
+
+ <meta-data
+ android:name="android.service.wallpaper"
+ android:resource="@xml/watch_face" />
+
+ <meta-data
+ android:name="com.google.android.wearable.watchface.wearableConfigurationAction"
+ android:value="androidx.wear.watchface.editor.action.WATCH_FACE_EDITOR" />
+
+ <meta-data
+ android:name="com.google.android.wearable.watchface.companionBuiltinConfigurationEnabled"
+ android:value="true" />
+
+ <meta-data
+ android:name="androidx.wear.watchface.MULTIPLE_INSTANCES_ALLOWED"
+ android:value="true" />
+
+ <meta-data
+ android:name="androidx.wear.watchface.XmlSchemaAndComplicationSlotsDefinition"
+ android:resource="@xml/xml_watchface" />
+ </service>
+
+ </application>
+
+</manifest>
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/BaseFutureCallback.java b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/BaseFutureCallback.java
new file mode 100644
index 00000000000..3f7d1316eac
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/BaseFutureCallback.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.watchface.samples.minimal.instances;
+
+import android.content.Context;
+import android.util.Log;
+import android.widget.Toast;
+
+/** A base class for a {@link FutureCallback} that logs the outcome. */
+abstract class BaseFutureCallback<T> implements FutureCallback<T> {
+
+ private final Context mContext;
+ private final String mTag;
+ private final String mName;
+
+ BaseFutureCallback(Context context, String tag, String name) {
+ mContext = context;
+ mTag = tag;
+ mName = name;
+ }
+
+ @Override
+ public void onPending() {
+ Log.d(mTag, mName + ".onPending()");
+ }
+
+ @Override
+ public void onSuccess(T value) {
+ Log.d(mTag, mName + ".onSuccess(" + value + ")");
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ Log.d(mTag, mName + ".onFailure(" + throwable.getMessage() + ")", throwable);
+ Toast.makeText(mContext, "Failure", Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onCancelled() {
+ Log.d(mTag, mName + ".onCancelled()");
+ Toast.makeText(mContext, "Cancelled", Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onInterrupted() {
+ Thread.currentThread().interrupt();
+ Log.d(mTag, mName + ".onInterrupted()");
+ Toast.makeText(mContext, "Interrupted", Toast.LENGTH_LONG).show();
+ }
+}
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/ConfigActivity.java b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/ConfigActivity.java
new file mode 100644
index 00000000000..837dbdba275
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/ConfigActivity.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.watchface.samples.minimal.instances;
+
+import static androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting;
+import static androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting.ListOption;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.TextView;
+
+import androidx.activity.ComponentActivity;
+import androidx.annotation.Nullable;
+import androidx.wear.watchface.editor.ListenableEditorSession;
+import androidx.wear.watchface.style.MutableUserStyle;
+import androidx.wear.watchface.style.UserStyleSetting;
+import androidx.wear.widget.CurvedTextView;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+import kotlin.OptIn;
+
+/** Configuration activity for the watch face. */
+public class ConfigActivity extends ComponentActivity {
+
+ private static final String TAG = "ConfigActivity";
+
+ private final Executor mMainExecutor = new Executor() {
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ @Override
+ public void execute(Runnable runnable) {
+ mHandler.post(runnable);
+ }
+ };
+
+ private CurvedTextView mInstanceId;
+ private TextView mStyleValue;
+ private final UserStyleSetting.Id mTimeStyleId = new UserStyleSetting.Id("TimeStyle");
+
+ @Nullable
+ private ListenableEditorSession mEditorSession;
+
+ public ConfigActivity() {
+ addCallback(
+ ListenableEditorSession.listenableCreateOnWatchEditorSession(this),
+ new BaseFutureCallback<ListenableEditorSession>(
+ this, TAG, "listenableCreateOnWatchEditingSession") {
+ @Override
+ public void onSuccess(ListenableEditorSession editorSession) {
+ super.onSuccess(editorSession);
+ mEditorSession = editorSession;
+ updateInstanceId();
+ updateStyleValue();
+ }
+ });
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.config_activity_layout);
+
+ mInstanceId = findViewById(R.id.instance_id);
+ mStyleValue = findViewById(R.id.style_value);
+
+ findViewById(R.id.style_change).setOnClickListener((view) -> changeStyle());
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ updateStyleValue();
+ }
+
+ @Override
+ protected void onDestroy() {
+ finish();
+ super.onDestroy();
+ }
+
+ private void changeStyle() {
+ Log.d(TAG, "changeStyle");
+ if (mEditorSession == null) {
+ return;
+ }
+
+ MutableUserStyle userStyle = mEditorSession.getUserStyle().getValue().toMutableUserStyle();
+ ListOption currentOption = (ListOption) userStyle.get(mTimeStyleId);
+ @OptIn(markerClass = androidx.wear.watchface.style.ExperimentalHierarchicalStyle.class)
+ ListUserStyleSetting listUserStyleSetting =
+ (ListUserStyleSetting) mEditorSession.getUserStyleSchema()
+ .getRootUserStyleSettings()
+ .get(0);
+
+ // Choose the first option in the list of options that isn't currentOption. We only expect
+ // two options here, so this will flip flop between them.
+ for (UserStyleSetting.Option option : listUserStyleSetting.getOptions()) {
+ if (!option.getId().equals(currentOption.getId())) {
+ userStyle.set(mTimeStyleId, option);
+ break;
+ }
+ }
+ mEditorSession.getUserStyle().setValue(userStyle.toUserStyle());
+ updateStyleValue();
+ }
+
+ private void updateInstanceId() {
+ if (mEditorSession == null) {
+ return;
+ }
+ mInstanceId.setText(Objects.toString(mEditorSession.getWatchFaceId()));
+ }
+
+ private void updateStyleValue() {
+ if (mEditorSession == null) {
+ return;
+ }
+ ListOption option =
+ (ListOption) mEditorSession.getUserStyle().getValue().get(mTimeStyleId);
+ mStyleValue.setText(option.getDisplayName());
+ }
+
+ private <T> void addCallback(ListenableFuture<T> future, FutureCallback<T> callback) {
+ FutureCallback.addCallback(future, callback, mMainExecutor);
+ }
+}
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/FutureCallback.java b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/FutureCallback.java
new file mode 100644
index 00000000000..4c12b45af52
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/FutureCallback.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.watchface.samples.minimal.instances;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+/** A callback for a future that explicitly handles different types of outcomes. */
+interface FutureCallback<T> {
+
+ static <T> void addCallback(
+ ListenableFuture<T> future, FutureCallback<T> callback, Executor executor) {
+ if (!future.isDone()) {
+ callback.onPending();
+ }
+ future.addListener(
+ () -> {
+ if (future.isCancelled()) {
+ callback.onCancelled();
+ } else {
+ try {
+ callback.onSuccess(future.get());
+ } catch (InterruptedException e) {
+ callback.onInterrupted();
+ } catch (ExecutionException e) {
+ callback.onFailure(e.getCause());
+ }
+ }
+ },
+ executor);
+ }
+
+ /** Called immediately if a callback is added to a future that is not yet done. */
+ void onPending();
+
+ /** Called if the future returns a value. */
+ void onSuccess(T value);
+
+ /** Called if the future throws an exception. */
+ void onFailure(Throwable throwable);
+
+ /** Called if the future is interrupted. */
+ void onInterrupted();
+
+ /** Called if the future is cancelled. */
+ void onCancelled();
+}
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/WatchFaceRenderer.java b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/WatchFaceRenderer.java
new file mode 100644
index 00000000000..8e13885bc18
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/WatchFaceRenderer.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.watchface.samples.minimal.instances;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.SurfaceHolder;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Px;
+import androidx.annotation.UiThread;
+import androidx.lifecycle.FlowLiveDataConversions;
+import androidx.wear.watchface.CanvasType;
+import androidx.wear.watchface.DrawMode;
+import androidx.wear.watchface.ListenableCanvasRenderer;
+import androidx.wear.watchface.RenderParameters;
+import androidx.wear.watchface.WatchState;
+import androidx.wear.watchface.style.CurrentUserStyleRepository;
+import androidx.wear.watchface.style.UserStyle;
+import androidx.wear.watchface.style.UserStyleSetting;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.time.ZonedDateTime;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import kotlin.Unit;
+
+/**
+ * Minimal rendered for the watch face, using canvas to render hours, minutes, and a blinking
+ * separator.
+ */
+public class WatchFaceRenderer extends ListenableCanvasRenderer {
+
+ private static final long UPDATE_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(1);
+ private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+ private final InstanceIdRenderer mInstanceIdRenderer;
+ private final TimeRenderer mMinimalRenderer;
+ private final TimeRenderer mSecondsRenderer;
+ private final Paint mHighlightPaint;
+ private final CurrentUserStyleRepository mCurrentUserStyleRepository;
+ private final UserStyleSetting.Id mTimeStyleId;
+ private final Resources mResources;
+
+ private TimeRenderer mTimeRenderer;
+
+ public WatchFaceRenderer(
+ @NotNull SurfaceHolder surfaceHolder,
+ @NotNull CurrentUserStyleRepository currentUserStyleRepository,
+ @NotNull WatchState watchState,
+ @NotNull Resources resources) {
+ super(surfaceHolder, currentUserStyleRepository, watchState, CanvasType.HARDWARE,
+ UPDATE_DELAY_MILLIS);
+ mInstanceIdRenderer = new InstanceIdRenderer(watchState);
+ mMinimalRenderer = new MinimalRenderer(watchState, mInstanceIdRenderer);
+ mSecondsRenderer = new SecondsRenderer(watchState, mInstanceIdRenderer);
+ mHighlightPaint = new Paint();
+ mCurrentUserStyleRepository = currentUserStyleRepository;
+ mTimeStyleId = new UserStyleSetting.Id(resources.getString(R.string.setting_id_time_style));
+ mResources = resources;
+ }
+
+ @UiThread
+ @NonNull
+ @Override
+ public ListenableFuture<Unit> initFuture() {
+ // observeForever has to be called from the UI thread but the WatchFaceRenderer is called
+ // from a background thread.
+ FlowLiveDataConversions.asLiveData(mCurrentUserStyleRepository.getUserStyle())
+ .observeForever(this::updateTimeStyle);
+ mInstanceIdRenderer.registerObservers();
+ return Futures.immediateFuture(Unit.INSTANCE);
+ }
+
+ @Override
+ public void render(@NotNull Canvas canvas, @NotNull Rect rect,
+ @NotNull ZonedDateTime zonedDateTime) {
+ mTimeRenderer.render(canvas, rect, zonedDateTime, getRenderParameters());
+ }
+
+ @Override
+ public void renderHighlightLayer(@NonNull Canvas canvas, @NonNull Rect bounds,
+ @NonNull ZonedDateTime zonedDateTime) {
+ RenderParameters.HighlightLayer highlightLayer = getRenderParameters().getHighlightLayer();
+ canvas.drawColor(highlightLayer.getBackgroundTint());
+ mHighlightPaint.setColor(highlightLayer.getHighlightTint());
+ mHighlightPaint.setStrokeWidth(2f);
+ canvas.drawCircle(
+ bounds.centerX(), bounds.centerY(), bounds.width() / 2 - 2, mHighlightPaint);
+ }
+
+ private void updateTimeStyle(UserStyle userStyle) {
+ String option = userStyle.get(mTimeStyleId).getId().toString();
+ if (option.equals(mResources.getString(R.string.option_id_time_style_minimal))) {
+ mTimeRenderer = mMinimalRenderer;
+ } else if (option.equals(mResources.getString(R.string.option_id_time_style_seconds))) {
+ mTimeRenderer = mSecondsRenderer;
+ }
+ }
+
+ private interface TimeRenderer {
+ void render(
+ @NotNull Canvas canvas, @NotNull Rect rect, @NotNull ZonedDateTime zonedDateTime,
+ RenderParameters renderParameters);
+ }
+
+ private static class InstanceIdRenderer implements TimeRenderer {
+ private final WatchState mWatchState;
+ private final Path mInstanceIdPath;
+ private final RectF mInstanceIdPathRect;
+ private final Paint mInstanceIdPaint;
+ private final AtomicReference<String> mInstanceId;
+
+ private InstanceIdRenderer(WatchState watchState) {
+ mWatchState = watchState;
+ mInstanceIdPath = new Path();
+ mInstanceIdPathRect = new RectF();
+ mInstanceIdPaint = new Paint();
+ mInstanceIdPaint.setTextAlign(Align.CENTER);
+ mInstanceIdPaint.setTextSize(20f);
+ mInstanceIdPaint.setColor(Color.WHITE);
+ mInstanceId = new AtomicReference<>(
+ Objects.toString(watchState.getWatchFaceInstanceId().getValue()));
+ }
+
+ @UiThread
+ public void registerObservers() {
+ FlowLiveDataConversions.asLiveData(mWatchState.getWatchFaceInstanceId())
+ .observeForever(mInstanceId::set);
+ }
+
+ @Override
+ public void render(@NonNull Canvas canvas, @NonNull Rect rect,
+ @NonNull ZonedDateTime zonedDateTime, RenderParameters renderParameters) {
+ canvas.save();
+ canvas.scale(0.9f, 0.9f);
+ canvas.translate(0.144f * rect.width() / 2, 0.1f * rect.height() / 2);
+ mInstanceIdPath.reset();
+ mInstanceIdPathRect.set(rect);
+ mInstanceIdPath.addArc(mInstanceIdPathRect, -180, 180);
+ canvas.drawTextOnPath(mInstanceId.get(), mInstanceIdPath, 0f, 0f, mInstanceIdPaint);
+ canvas.restore();
+ }
+ }
+
+ private static class MinimalRenderer implements TimeRenderer {
+ private final WatchState mWatchState;
+ private final InstanceIdRenderer mInstanceIdRenderer;
+ private final Paint mPaint;
+ private final char[] mTimeText = new char[5];
+
+ private MinimalRenderer(WatchState watchState, InstanceIdRenderer instanceIdRenderer) {
+ mWatchState = watchState;
+ mInstanceIdRenderer = instanceIdRenderer;
+ mPaint = new Paint();
+ mPaint.setTextAlign(Align.CENTER);
+ mPaint.setTextSize(64f);
+ }
+
+ @Override
+ public void render(
+ @NotNull Canvas canvas, @NotNull Rect rect, @NotNull ZonedDateTime zonedDateTime,
+ RenderParameters renderParameters) {
+ mPaint.setColor(Color.BLACK);
+ canvas.drawRect(rect, mPaint);
+ mPaint.setColor(Color.WHITE);
+ int hour = zonedDateTime.getHour() % 12;
+ int minute = zonedDateTime.getMinute();
+ int second = zonedDateTime.getSecond();
+ mTimeText[0] = DIGITS[hour / 10];
+ mTimeText[1] = DIGITS[hour % 10];
+ mTimeText[2] = second % 2 == 0 ? ':' : ' ';
+ mTimeText[3] = DIGITS[minute / 10];
+ mTimeText[4] = DIGITS[minute % 10];
+ canvas.drawText(mTimeText,
+ 0,
+ 5,
+ rect.centerX(),
+ rect.centerY() - mWatchState.getChinHeight(),
+ mPaint);
+
+ mInstanceIdRenderer.render(canvas, rect, zonedDateTime, renderParameters);
+ }
+ }
+
+ private static class SecondsRenderer implements TimeRenderer {
+ @Px
+ public static final float SECONDS_TEXT_HEIGHT = 256f;
+ @Px
+ public static final float TIME_TEXT_ACTIVE_HEIGHT = 64f;
+ @Px
+ public static final float TIME_TEXT_AMBIENT_HEIGHT = 96f;
+ @Px
+ private static final int TEXT_PADDING = 12;
+
+ private final WatchState mWatchState;
+ private final InstanceIdRenderer mInstanceIdRenderer;
+ private final Paint mPaint;
+ private final char[] mTimeText = new char[]{'1', '0', ':', '0', '9'};
+ private final char[] mSecondsText = new char[]{'3', '0'};
+ @Px
+ private final int mTimeActiveOffset;
+ @Px
+ private final int mTimeAmbientOffset;
+ @Px
+ private final int mSecondsActiveOffset;
+
+ private SecondsRenderer(WatchState watchState, InstanceIdRenderer instanceIdRenderer) {
+ mWatchState = watchState;
+ mInstanceIdRenderer = instanceIdRenderer;
+ mPaint = new Paint();
+ mPaint.setTextAlign(Align.CENTER);
+
+ // Compute location of text.
+ Rect textBounds = new Rect();
+
+ mPaint.setTextSize(TIME_TEXT_ACTIVE_HEIGHT);
+ mPaint.getTextBounds(mTimeText, 0, mTimeText.length, textBounds);
+ @Px int timeActiveHeight = textBounds.height();
+
+ mPaint.setTextSize(TIME_TEXT_AMBIENT_HEIGHT);
+ mPaint.getTextBounds(mTimeText, 0, mTimeText.length, textBounds);
+ @Px int timeAmbientHeight = textBounds.height();
+
+ mPaint.setTextSize(SECONDS_TEXT_HEIGHT);
+ mPaint.getTextBounds(mSecondsText, 0, mSecondsText.length, textBounds);
+ @Px int secondsHeight = textBounds.height();
+
+ mTimeActiveOffset =
+ (timeActiveHeight + secondsHeight + TEXT_PADDING) / 2 - timeActiveHeight;
+ mTimeAmbientOffset = timeAmbientHeight / 2 - timeAmbientHeight;
+ mSecondsActiveOffset = mTimeActiveOffset - secondsHeight - TEXT_PADDING;
+ }
+
+ @Override
+ public void render(
+ @NotNull Canvas canvas, @NotNull Rect rect, @NotNull ZonedDateTime zonedDateTime,
+ RenderParameters renderParameters) {
+ boolean isActive = renderParameters.getDrawMode() != DrawMode.AMBIENT;
+ int hour = zonedDateTime.getHour() % 12;
+ int minute = zonedDateTime.getMinute();
+ int second = zonedDateTime.getSecond();
+
+ if (isActive) {
+ mPaint.setColor(Color.rgb(64 + 192 * second / 60, 0, 0));
+ } else {
+ mPaint.setColor(Color.BLACK);
+ }
+ canvas.drawRect(rect, mPaint);
+ mPaint.setColor(Color.WHITE);
+ mTimeText[0] = DIGITS[hour / 10];
+ mTimeText[1] = DIGITS[hour % 10];
+ mTimeText[2] = second % 2 == 0 ? ':' : ' ';
+ mTimeText[3] = DIGITS[minute / 10];
+ mTimeText[4] = DIGITS[minute % 10];
+ mPaint.setTextSize(isActive ? TIME_TEXT_ACTIVE_HEIGHT : TIME_TEXT_AMBIENT_HEIGHT);
+ @Px int timeOffset = isActive ? mTimeActiveOffset : mTimeAmbientOffset;
+ canvas.drawText(mTimeText,
+ 0,
+ mTimeText.length,
+ rect.centerX(),
+ rect.centerY() - mWatchState.getChinHeight() - timeOffset,
+ mPaint);
+ mPaint.setTextSize(SECONDS_TEXT_HEIGHT);
+ if (isActive) {
+ mSecondsText[0] = DIGITS[second / 10];
+ mSecondsText[1] = DIGITS[second % 10];
+ canvas.drawText(mSecondsText,
+ 0,
+ mSecondsText.length,
+ rect.centerX(),
+ rect.centerY() - mWatchState.getChinHeight() - mSecondsActiveOffset,
+ mPaint);
+ }
+
+ mInstanceIdRenderer.render(canvas, rect, zonedDateTime, renderParameters);
+ }
+ }
+}
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/WatchFaceService.java b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/WatchFaceService.java
new file mode 100644
index 00000000000..6dd17611d1c
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/java/androidx/wear/watchface/samples/minimal/instances/WatchFaceService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.wear.watchface.samples.minimal.instances;
+
+import android.view.SurfaceHolder;
+
+import androidx.annotation.NonNull;
+import androidx.wear.watchface.ComplicationSlotsManager;
+import androidx.wear.watchface.ListenableWatchFaceService;
+import androidx.wear.watchface.Renderer;
+import androidx.wear.watchface.WatchFace;
+import androidx.wear.watchface.WatchFaceType;
+import androidx.wear.watchface.WatchState;
+import androidx.wear.watchface.style.CurrentUserStyleRepository;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.jetbrains.annotations.NotNull;
+
+/** The service that defines the watch face. */
+public class WatchFaceService extends ListenableWatchFaceService {
+
+ @NotNull
+ @Override
+ protected ListenableFuture<WatchFace> createWatchFaceFuture(
+ @NotNull SurfaceHolder surfaceHolder, @NotNull WatchState watchState,
+ @NonNull ComplicationSlotsManager complicationSlotsManager,
+ @NonNull CurrentUserStyleRepository currentUserStyleRepository) {
+ Renderer renderer =
+ new WatchFaceRenderer(
+ surfaceHolder, currentUserStyleRepository, watchState, getResources());
+ WatchFace watchFace = new WatchFace(WatchFaceType.DIGITAL, renderer);
+ return Futures.immediateFuture(watchFace);
+ }
+}
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_icon.png b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_icon.png
new file mode 100644
index 00000000000..1cb166fc002
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_icon.png
Binary files differ
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_minimal_icon.png b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_minimal_icon.png
new file mode 100644
index 00000000000..015a12359ae
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_minimal_icon.png
Binary files differ
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_seconds_icon.png b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_seconds_icon.png
new file mode 100644
index 00000000000..ffa49490369
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_seconds_icon.png
Binary files differ
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/ic_launcher_background.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000000..69d8a246915
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <group android:scaleX="0.7"
+ android:scaleY="0.7"
+ android:translateX="16.2"
+ android:translateY="16.2">
+ <path android:fillColor="#3DDC84"
+ android:pathData="M0,0h108v108h-108z"/>
+ <path android:fillColor="#00000000" android:pathData="M9,0L9,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,0L19,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,0L29,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,0L39,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,0L49,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,0L59,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,0L69,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,0L79,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M89,0L89,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M99,0L99,108"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,9L108,9"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,19L108,19"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,29L108,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,39L108,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,49L108,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,59L108,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,69L108,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,79L108,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,89L108,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M0,99L108,99"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,29L89,29"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,39L89,39"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,49L89,49"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,59L89,59"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,69L89,69"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M19,79L89,79"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M29,19L29,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M39,19L39,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M49,19L49,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M59,19L59,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M69,19L69,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ <path android:fillColor="#00000000" android:pathData="M79,19L79,89"
+ android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
+ </group>
+</vector>
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/ic_launcher_foreground.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 00000000000..74f38c87ca6
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108"
+ android:tint="#000000">
+ <group android:scaleX="2.61"
+ android:scaleY="2.61"
+ android:translateX="22.68"
+ android:translateY="22.68">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10s10,-4.5 10,-10S17.5,2 12,2zM16.2,16.2L11,13V7h1.5v5.2l4.5,2.7L16.2,16.2z"/>
+ </group>
+</vector>
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/preview.png b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/preview.png
new file mode 100644
index 00000000000..24eadcb99f0
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/preview.png
Binary files differ
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/tyme_style_bold_icon.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/tyme_style_bold_icon.xml
new file mode 100644
index 00000000000..07eb5059cc8
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/tyme_style_bold_icon.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+</selector> \ No newline at end of file
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/layout/config_activity_layout.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/layout/config_activity_layout.xml
new file mode 100644
index 00000000000..529c0535c78
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/layout/config_activity_layout.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.wear.widget.CurvedTextView
+ android:id="@+id/instance_id"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:text="instance-id"
+ android:background="#66FF0000"
+ android:textAlignment="textStart"
+ android:textSize="10sp"
+ app:clockwise="true"
+ app:minSweepDegrees="10"
+ />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="30dp"
+ tools:text="instance-id"
+ android:textSize="9sp"
+ android:textColor="@android:color/white"
+ android:textFontWeight="800" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/style_header"
+ android:textColor="@android:color/white"
+ android:textFontWeight="800" />
+
+ <TextView
+ android:id="@+id/style_value"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@+id/style_change"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/style_change" />
+ </LinearLayout>
+</FrameLayout> \ No newline at end of file
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-anydpi/ic_launcher.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-anydpi/ic_launcher.xml
new file mode 100644
index 00000000000..bbd3e021239
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-anydpi/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@drawable/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-hdpi/ic_launcher.png b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000000..837401ad190
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-mdpi/ic_launcher.png b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000000..f208553c8f8
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xhdpi/ic_launcher.png b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000000..c9f75b42996
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxhdpi/ic_launcher.png b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..5c6c3428092
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..4b7cddfd78c
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/values/dimens.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/values/dimens.xml
new file mode 100644
index 00000000000..6da0d5894e3
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="highlight_stroke_width">2dp</dimen>
+ <dimen name="highlight_extra_radius">1px</dimen>
+</resources>
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/values/schema_ids.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/values/schema_ids.xml
new file mode 100644
index 00000000000..06e9ca15373
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/values/schema_ids.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <string name="setting_id_time_style" translatable="false">TimeStyle</string>
+ <string name="option_id_time_style_minimal" translatable="false">minimal</string>
+ <string name="option_id_time_style_seconds" translatable="false">seconds</string>
+</resources> \ No newline at end of file
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/values/strings.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/values/strings.xml
new file mode 100644
index 00000000000..32f9ab6a8f3
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/values/strings.xml
@@ -0,0 +1,11 @@
+<resources>
+ <string name="app_name">Minimal Instances</string>
+ <string name="configuration_title">Configuration</string>
+ <string name="style_change">Change</string>
+ <string name="style_header">Time Style</string>
+ <string name="time_style_name">Time</string>
+ <string name="time_style_description">How the time is displayed on the watch face</string>
+ <string name="time_style_minimal_name">Minimal</string>
+ <string name="time_style_seconds_name">Seconds</string>
+ <string name="time_style_bold_name">Bold</string>
+</resources>
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/xml/watch_face.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/xml/watch_face.xml
new file mode 100644
index 00000000000..7e7098ff3f6
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/xml/watch_face.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<wallpaper/> \ No newline at end of file
diff --git a/wear/watchface/watchface-samples-minimal-instances/src/main/res/xml/xml_watchface.xml b/wear/watchface/watchface-samples-minimal-instances/src/main/res/xml/xml_watchface.xml
new file mode 100644
index 00000000000..cf25b886f1f
--- /dev/null
+++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/xml/xml_watchface.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<XmlWatchFace xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <UserStyleSchema>
+ <ListUserStyleSetting
+ android:icon="@drawable/time_style_icon"
+ app:affectedWatchFaceLayers="BASE|COMPLICATIONS|COMPLICATIONS_OVERLAY"
+ app:defaultOptionIndex="1"
+ app:description="@string/time_style_description"
+ app:displayName="@string/time_style_name"
+ app:id="@string/setting_id_time_style">
+ <ListOption
+ android:icon="@drawable/time_style_minimal_icon"
+ app:displayName="@string/time_style_minimal_name"
+ app:id="@string/option_id_time_style_minimal" />
+ <ListOption
+ android:icon="@drawable/time_style_seconds_icon"
+ app:displayName="@string/time_style_seconds_name"
+ app:id="@string/option_id_time_style_seconds" />
+ </ListUserStyleSetting>
+ </UserStyleSchema>
+</XmlWatchFace>