diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2022-05-10 03:25:31 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2022-05-10 03:25:31 +0000 |
commit | 729038cb548b02f3021e0458846205540dd12293 (patch) | |
tree | 553f8ff304f32be9390af44be5d1248137fd8d7e | |
parent | 925b6f9a6e9d1af588be0cb7e018f3242799c52c (diff) | |
parent | e4e6c123a093030a0bfa0d54d9cea34d6ef781f9 (diff) | |
download | support-729038cb548b02f3021e0458846205540dd12293.tar.gz |
Merge "Add a minimal watch face using multiple instances." into androidx-main
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 Binary files differnew file mode 100644 index 00000000000..1cb166fc002 --- /dev/null +++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable-nodpi/time_style_icon.png 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 Binary files differnew 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 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 Binary files differnew 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 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 Binary files differnew file mode 100644 index 00000000000..24eadcb99f0 --- /dev/null +++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/drawable/preview.png 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 Binary files differnew file mode 100644 index 00000000000..837401ad190 --- /dev/null +++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-hdpi/ic_launcher.png 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 Binary files differnew file mode 100644 index 00000000000..f208553c8f8 --- /dev/null +++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-mdpi/ic_launcher.png 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 Binary files differnew file mode 100644 index 00000000000..c9f75b42996 --- /dev/null +++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xhdpi/ic_launcher.png 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 Binary files differnew file mode 100644 index 00000000000..5c6c3428092 --- /dev/null +++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxhdpi/ic_launcher.png 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 Binary files differnew file mode 100644 index 00000000000..4b7cddfd78c --- /dev/null +++ b/wear/watchface/watchface-samples-minimal-instances/src/main/res/mipmap-xxxhdpi/ic_launcher.png 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> |