Add a minimal sample watch face with styling.
For testing purposes, it is helpful to have a simple watch face that
only provides a simple styling option working end to end. This can be
used when making changes to the style implementation or by a developer
when wanting to try things out with minimal changes.
Test: manually run the sample
Change-Id: Iaa8621196363d62d196a087bbffd123da17457d1
diff --git a/settings.gradle b/settings.gradle
index 0c88ab9..59dc14e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -657,6 +657,7 @@
includeProject(":wear:wear-watchface-samples-app", "wear/wear-watchface/samples/app", [BuildType.MAIN, BuildType.WEAR])
includeProject(":wear:wear-watchface-samples-minimal", "wear/wear-watchface/samples/minimal", [BuildType.MAIN, BuildType.WEAR])
includeProject(":wear:wear-watchface-samples-minimal-complications", "wear/wear-watchface-samples-minimal-complications", [BuildType.MAIN, BuildType.WEAR])
+includeProject(":wear:wear-watchface-samples-minimal-style", "wear/wear-watchface-samples-minimal-style", [BuildType.MAIN, BuildType.WEAR])
includeProject(":wear:wear-watchface-style", "wear/wear-watchface-style", [BuildType.MAIN, BuildType.WEAR])
includeProject(":webkit:integration-tests:testapp", "webkit/integration-tests/testapp", [BuildType.MAIN])
includeProject(":webkit:webkit", "webkit/webkit", [BuildType.MAIN])
diff --git a/wear/wear-watchface-samples-minimal-style/build.gradle b/wear/wear-watchface-samples-minimal-style/build.gradle
new file mode 100644
index 0000000..c696635
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/build.gradle
@@ -0,0 +1,61 @@
+/*
+ * 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.LibraryGroups
+import androidx.build.LibraryType
+import androidx.build.LibraryVersions
+
+plugins {
+ id("AndroidXPlugin")
+ id("com.android.application")
+}
+
+dependencies {
+ api(project(":activity:activity"))
+ api(project(":wear:wear-watchface"))
+ api(project(":wear:wear-watchface-editor"))
+ api(project(":wear:wear-watchface-editor-guava"))
+ api(project(":wear:wear-watchface-guava"))
+ implementation(libs.guavaAndroid)
+}
+
+androidx {
+ name = "AndroidX Wear Watchface Minimal Style Sample"
+ type = LibraryType.SAMPLES
+ mavenGroup = LibraryGroups.WEAR
+ mavenVersion = LibraryVersions.WEAR_WATCHFACE
+ inceptionYear = "2021"
+ description = "Contains the sample code for the Androidx Wear Watchface library"
+}
+
+android {
+ defaultConfig {
+ minSdkVersion 25
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled true
+ shrinkResources true
+ proguardFiles getDefaultProguardFile('proguard-android.txt')
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+}
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/AndroidManifest.xml b/wear/wear-watchface-samples-minimal-style/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..1852ba9
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/AndroidManifest.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="androidx.wear.watchface.samples.minimal.style">
+
+ <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=".ConfigActivity"
+ 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=".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" />
+
+ </service>
+
+ </application>
+
+</manifest>
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/BaseFutureCallback.java b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/BaseFutureCallback.java
new file mode 100644
index 0000000..f1499b7
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/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.style;
+
+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/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/ConfigActivity.java b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/ConfigActivity.java
new file mode 100644
index 0000000..8aff71d
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/ConfigActivity.java
@@ -0,0 +1,154 @@
+/*
+ * 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.style;
+
+import android.content.Context;
+import android.os.Build;
+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.annotation.RequiresApi;
+import androidx.wear.watchface.editor.ListenableEditorSession;
+import androidx.wear.watchface.style.UserStyle;
+import androidx.wear.watchface.style.UserStyleSetting;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/** Configuration activity for the watch face. */
+public class ConfigActivity extends ComponentActivity {
+
+ private static final String TAG = "ConfigActivity";
+
+ private static final EnumMap<TimeStyle.Value, TimeStyle.Value> NEXT_VALUE_MAP =
+ createNextValueMap();
+
+ private Executor mMainExecutor;
+ private TimeStyle mTimeStyle;
+
+ private TextView mStyleValue;
+
+ @Nullable
+ private ListenableEditorSession mEditorSession;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.config_activity_layout);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ mMainExecutor = MainExecutorApi28.getMainExecutor(this);
+ } else {
+ mMainExecutor = MainExecutorApi25.getMainExecutor();
+ }
+ mTimeStyle = new TimeStyle(this);
+
+ mStyleValue = findViewById(R.id.style_value);
+
+ findViewById(R.id.style_change).setOnClickListener((view) -> changeStyle());
+
+ addCallback(
+ ListenableEditorSession.listenableCreateOnWatchEditorSession(this, getIntent()),
+ new BaseFutureCallback<ListenableEditorSession>(
+ this, TAG, "listenableCreateOnWatchEditingSession") {
+ @Override
+ public void onSuccess(ListenableEditorSession editorSession) {
+ super.onSuccess(editorSession);
+ mEditorSession = editorSession;
+ updateStyleValue();
+ }
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ updateStyleValue();
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mEditorSession != null) {
+ mEditorSession.setCommitChangesOnClose(true);
+ mEditorSession.close();
+ }
+ finish();
+ super.onDestroy();
+ }
+
+ private void changeStyle() {
+ Log.d(TAG, "changeStyle");
+ if (mEditorSession == null) {
+ return;
+ }
+
+ UserStyle userStyle = copyOfUserStyle(mEditorSession.getUserStyle());
+ TimeStyle.Value value = mTimeStyle.get(userStyle);
+ TimeStyle.Value newValue = NEXT_VALUE_MAP.get(value);
+ mTimeStyle.set(userStyle, newValue);
+ mEditorSession.setUserStyle(userStyle);
+ updateStyleValue();
+ }
+
+ private void updateStyleValue() {
+ if (mEditorSession == null) {
+ return;
+ }
+ TimeStyle.Value value = mTimeStyle.get(mEditorSession.getUserStyle());
+ mStyleValue.setText(mTimeStyle.getDisplayName(value));
+ }
+
+ private <T> void addCallback(ListenableFuture<T> future, FutureCallback<T> callback) {
+ FutureCallback.addCallback(future, callback, mMainExecutor);
+ }
+
+ private static UserStyle copyOfUserStyle(UserStyle userStyle) {
+ Map<UserStyleSetting, UserStyleSetting.Option> styleMap = new HashMap<>();
+ styleMap.putAll(userStyle.getSelectedOptions());
+ return new UserStyle(styleMap);
+ }
+
+ private static EnumMap<TimeStyle.Value, TimeStyle.Value> createNextValueMap() {
+ EnumMap<TimeStyle.Value, TimeStyle.Value> map = new EnumMap<>(TimeStyle.Value.class);
+ map.put(TimeStyle.Value.MINIMAL, TimeStyle.Value.SECONDS);
+ map.put(TimeStyle.Value.SECONDS, TimeStyle.Value.MINIMAL);
+ return map;
+ }
+
+ private static final class MainExecutorApi25 {
+ public static Handler sMainThreadHandler = new Handler(Looper.getMainLooper());
+
+ public static Executor getMainExecutor() {
+ return sMainThreadHandler::post;
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.P)
+ private static final class MainExecutorApi28 {
+ public static Executor getMainExecutor(Context context) {
+ return context.getMainExecutor();
+ }
+ }
+}
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/FutureCallback.java b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/FutureCallback.java
new file mode 100644
index 0000000..12f62de
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/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.style;
+
+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/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/TimeStyle.java b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/TimeStyle.java
new file mode 100644
index 0000000..9883bf3
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/TimeStyle.java
@@ -0,0 +1,123 @@
+/*
+ * 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.style;
+
+import android.content.Context;
+import android.graphics.drawable.Icon;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+import androidx.wear.watchface.style.UserStyle;
+import androidx.wear.watchface.style.UserStyleSetting;
+import androidx.wear.watchface.style.WatchFaceLayer;
+
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Map;
+
+final class TimeStyle {
+
+ public enum Value {
+ MINIMAL,
+ SECONDS,
+ }
+
+ private static final UserStyleSetting.Id ID = new UserStyleSetting.Id("TypeStyle");
+
+ private static final UserStyleSetting.Option.Id MINIMAL_ID =
+ new UserStyleSetting.Option.Id("minimal");
+ private static final UserStyleSetting.Option.Id SECONDS_ID =
+ new UserStyleSetting.Option.Id("seconds");
+
+ private static final EnumMap<Value, UserStyleSetting.Option.Id> VALUE_ID_MAP =
+ createOptionIdMap();
+
+ private static final Map<UserStyleSetting.Option.Id, Value> ID_VALUE_MAP =
+ createIdOptionMap();
+
+ private final UserStyleSetting mSetting;
+
+ TimeStyle(Context context) {
+ mSetting = create(context);
+ }
+
+ public UserStyleSetting get() {
+ return mSetting;
+ }
+
+ public Value get(UserStyle userStyle) {
+ UserStyleSetting.Option current = userStyle.get(mSetting);
+ return current == null ? Value.MINIMAL : ID_VALUE_MAP.get(current.getId());
+ }
+
+ public void set(UserStyle userStyle, Value value) {
+ userStyle.getSelectedOptions().put(mSetting, getOptionForValue(value));
+ }
+
+ public CharSequence getDisplayName(Value value) {
+ return getOptionForValue(value).getDisplayName();
+ }
+
+ private UserStyleSetting.ListUserStyleSetting.ListOption getOptionForValue(Value value) {
+ return (UserStyleSetting.ListUserStyleSetting.ListOption)
+ mSetting.getOptionForId(VALUE_ID_MAP.get(value));
+ }
+
+ private static EnumMap<Value, UserStyleSetting.Option.Id> createOptionIdMap() {
+ EnumMap<Value, UserStyleSetting.Option.Id> map = new EnumMap<>(Value.class);
+ map.put(Value.MINIMAL, MINIMAL_ID);
+ map.put(Value.SECONDS, SECONDS_ID);
+ return map;
+ }
+
+ private static Map<UserStyleSetting.Option.Id, Value> createIdOptionMap() {
+ Map<UserStyleSetting.Option.Id, Value> map = new HashMap<>();
+ map.put(MINIMAL_ID, Value.MINIMAL);
+ map.put(SECONDS_ID, Value.SECONDS);
+ return map;
+ }
+
+ private static UserStyleSetting create(Context context) {
+ return new UserStyleSetting.ListUserStyleSetting(
+ ID,
+ getString(context, R.string.time_style_name),
+ getString(context, R.string.time_style_description),
+ getIcon(context, R.drawable.time_style_icon),
+ Arrays.asList(
+ new UserStyleSetting.ListUserStyleSetting.ListOption(
+ MINIMAL_ID,
+ getString(context, R.string.time_style_minimal_name),
+ getIcon(context, R.drawable.tyme_style_minimal_icon)
+ ),
+ new UserStyleSetting.ListUserStyleSetting.ListOption(
+ SECONDS_ID,
+ getString(context, R.string.time_style_seconds_name),
+ getIcon(context, R.drawable.tyme_style_seconds_icon)
+ )
+ ),
+ WatchFaceLayer.ALL_WATCH_FACE_LAYERS);
+ }
+
+ private static String getString(Context context, @StringRes int resId) {
+ return context.getString(resId);
+ }
+
+ private static Icon getIcon(Context context, @DrawableRes int resId) {
+ return Icon.createWithResource(context, resId);
+ }
+}
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/WatchFaceRenderer.java b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/WatchFaceRenderer.java
new file mode 100644
index 0000000..56c55db
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/WatchFaceRenderer.java
@@ -0,0 +1,228 @@
+/*
+ * 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.style;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Rect;
+import android.icu.util.Calendar;
+import android.view.SurfaceHolder;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Px;
+import androidx.wear.watchface.CanvasType;
+import androidx.wear.watchface.DrawMode;
+import androidx.wear.watchface.RenderParameters;
+import androidx.wear.watchface.Renderer;
+import androidx.wear.watchface.WatchState;
+import androidx.wear.watchface.style.CurrentUserStyleRepository;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Minimal rendered for the watch face, using canvas to render hours, minutes, and a blinking
+ * separator.
+ */
+public class WatchFaceRenderer extends Renderer.CanvasRenderer {
+
+ 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 TimeRenderer mMinimalRenderer;
+ private final TimeRenderer mSecondsRenderer;
+ private final Paint mHighlightPaint;
+
+ private TimeRenderer mTimeRenderer;
+
+ public WatchFaceRenderer(
+ @NotNull SurfaceHolder surfaceHolder,
+ @NotNull CurrentUserStyleRepository currentUserStyleRepository,
+ @NotNull WatchState watchState,
+ @NotNull TimeStyle timeStyle) {
+ super(surfaceHolder, currentUserStyleRepository, watchState, CanvasType.HARDWARE,
+ UPDATE_DELAY_MILLIS);
+ currentUserStyleRepository.addUserStyleChangeListener(
+ userStyle -> updateTimeStyle(timeStyle.get(userStyle)));
+ mMinimalRenderer = new MinimalRenderer(watchState);
+ mSecondsRenderer = new SecondsRenderer(watchState);
+ mHighlightPaint = new Paint();
+ updateTimeStyle(timeStyle.get(currentUserStyleRepository.getUserStyle()));
+ }
+
+ @Override
+ public void render(@NotNull Canvas canvas, @NotNull Rect rect, @NotNull Calendar calendar) {
+ mTimeRenderer.render(canvas, rect, calendar, getRenderParameters());
+ }
+
+ @Override
+ public void renderHighlightLayer(@NonNull Canvas canvas, @NonNull Rect bounds,
+ @NonNull Calendar calendar) {
+ 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(TimeStyle.Value value) {
+ switch (value) {
+ case MINIMAL:
+ mTimeRenderer = mMinimalRenderer;
+ break;
+ case SECONDS:
+ mTimeRenderer = mSecondsRenderer;
+ break;
+ }
+ }
+
+ private interface TimeRenderer {
+ void render(
+ @NotNull Canvas canvas, @NotNull Rect rect, @NotNull Calendar calendar,
+ RenderParameters renderParameters);
+ }
+
+ private static class MinimalRenderer implements TimeRenderer {
+ private final WatchState mWatchState;
+ private final Paint mPaint;
+ private final char[] mTimeText = new char[5];
+
+ private MinimalRenderer(WatchState watchState) {
+ mWatchState = watchState;
+ mPaint = new Paint();
+ mPaint.setTextAlign(Align.CENTER);
+ mPaint.setTextSize(64f);
+ }
+
+ @Override
+ public void render(
+ @NotNull Canvas canvas, @NotNull Rect rect, @NotNull Calendar calendar,
+ RenderParameters renderParameters) {
+ mPaint.setColor(Color.BLACK);
+ canvas.drawRect(rect, mPaint);
+ mPaint.setColor(Color.WHITE);
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ int minute = calendar.get(Calendar.MINUTE);
+ int second = calendar.get(Calendar.SECOND);
+ 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);
+ }
+ }
+
+ 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 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) {
+ mWatchState = watchState;
+ 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 Calendar calendar,
+ RenderParameters renderParameters) {
+ boolean isActive = renderParameters.getDrawMode() != DrawMode.AMBIENT;
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ int minute = calendar.get(Calendar.MINUTE);
+ int second = calendar.get(Calendar.SECOND);
+
+ 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);
+ }
+ }
+ }
+}
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/WatchFaceService.java b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/WatchFaceService.java
new file mode 100644
index 0000000..7284d9a
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/java/androidx/wear/watchface/samples/minimal/style/WatchFaceService.java
@@ -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.
+ */
+
+package androidx.wear.watchface.samples.minimal.style;
+
+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 androidx.wear.watchface.style.UserStyleSchema;
+
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+
+/** The service that defines the watch face. */
+public class WatchFaceService extends ListenableWatchFaceService {
+
+ private TimeStyle mTimeStyle;
+
+ @NonNull
+ @Override
+ protected UserStyleSchema createUserStyleSchema() {
+ mTimeStyle = new TimeStyle(this);
+ return new UserStyleSchema(Collections.singletonList(mTimeStyle.get()));
+ }
+
+ @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, mTimeStyle);
+ WatchFace watchFace = new WatchFace(WatchFaceType.DIGITAL, renderer);
+ return Futures.immediateFuture(watchFace);
+ }
+}
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/ic_launcher_background.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..69d8a24
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/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/wear-watchface-samples-minimal-style/src/main/res/drawable/ic_launcher_foreground.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..74f38c8
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/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/wear-watchface-samples-minimal-style/src/main/res/drawable/preview.png b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/preview.png
new file mode 100644
index 0000000..24eadcb
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/preview.png
Binary files differ
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/time_style_icon.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/time_style_icon.xml
new file mode 100644
index 0000000..07eb505
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/time_style_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/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_bold_icon.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_bold_icon.xml
new file mode 100644
index 0000000..07eb505
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/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/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_minimal_icon.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_minimal_icon.xml
new file mode 100644
index 0000000..07eb505
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_minimal_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/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_seconds_icon.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_seconds_icon.xml
new file mode 100644
index 0000000..07eb505
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/drawable/tyme_style_seconds_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/wear-watchface-samples-minimal-style/src/main/res/layout/config_activity_layout.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/layout/config_activity_layout.xml
new file mode 100644
index 0000000..504b39f
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/layout/config_activity_layout.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ 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>
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-anydpi-v26/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/wear-watchface-samples-minimal-style/src/main/res/mipmap-hdpi/ic_launcher.png b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..837401a
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-mdpi/ic_launcher.png b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..f208553
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-xhdpi/ic_launcher.png b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..c9f75b4
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-xxhdpi/ic_launcher.png b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..5c6c342
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4b7cddf
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/wear/wear-watchface-samples-minimal-style/src/main/res/values/dimens.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..6da0d58
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/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/wear-watchface-samples-minimal-style/src/main/res/values/strings.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c500305
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/src/main/res/values/strings.xml
@@ -0,0 +1,11 @@
+<resources>
+ <string name="app_name">Minimal Style</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/wear-watchface-samples-minimal-style/src/main/res/xml/watch_face.xml b/wear/wear-watchface-samples-minimal-style/src/main/res/xml/watch_face.xml
new file mode 100644
index 0000000..7e7098f
--- /dev/null
+++ b/wear/wear-watchface-samples-minimal-style/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