Support multiple ComplicationSlotsUserStyleSettings
Test: Added a new test
Bug: 258172541
Relnote: From android T multiple ComplicationSlotsUserStyleSettings are now supported in a style hierarchy as long as at most only one of them can be active at any one time. We've added a utility function findComplicationSlotsOptionForUserStyle to UserStyleSchema to help find the active ComplicationSlotsOption if any.
Change-Id: Ic2b065c5f6374669b9bd60f26ca061cb38e87460
diff --git a/wear/watchface/watchface-style/api/current.txt b/wear/watchface/watchface-style/api/current.txt
index f79c03d..274acb9 100644
--- a/wear/watchface/watchface-style/api/current.txt
+++ b/wear/watchface/watchface-style/api/current.txt
@@ -69,6 +69,7 @@
public final class UserStyleSchema {
ctor public UserStyleSchema(java.util.List<? extends androidx.wear.watchface.style.UserStyleSetting> userStyleSettings);
+ method public androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption? findComplicationSlotsOptionForUserStyle(androidx.wear.watchface.style.UserStyle userStyle);
method public operator androidx.wear.watchface.style.UserStyleSetting? get(androidx.wear.watchface.style.UserStyleSetting.Id settingId);
method public byte[] getDigestHash();
method public java.util.List<androidx.wear.watchface.style.UserStyleSetting> getRootUserStyleSettings();
diff --git a/wear/watchface/watchface-style/api/public_plus_experimental_current.txt b/wear/watchface/watchface-style/api/public_plus_experimental_current.txt
index f79c03d..274acb9 100644
--- a/wear/watchface/watchface-style/api/public_plus_experimental_current.txt
+++ b/wear/watchface/watchface-style/api/public_plus_experimental_current.txt
@@ -69,6 +69,7 @@
public final class UserStyleSchema {
ctor public UserStyleSchema(java.util.List<? extends androidx.wear.watchface.style.UserStyleSetting> userStyleSettings);
+ method public androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption? findComplicationSlotsOptionForUserStyle(androidx.wear.watchface.style.UserStyle userStyle);
method public operator androidx.wear.watchface.style.UserStyleSetting? get(androidx.wear.watchface.style.UserStyleSetting.Id settingId);
method public byte[] getDigestHash();
method public java.util.List<androidx.wear.watchface.style.UserStyleSetting> getRootUserStyleSettings();
diff --git a/wear/watchface/watchface-style/api/restricted_current.txt b/wear/watchface/watchface-style/api/restricted_current.txt
index f79c03d..274acb9 100644
--- a/wear/watchface/watchface-style/api/restricted_current.txt
+++ b/wear/watchface/watchface-style/api/restricted_current.txt
@@ -69,6 +69,7 @@
public final class UserStyleSchema {
ctor public UserStyleSchema(java.util.List<? extends androidx.wear.watchface.style.UserStyleSetting> userStyleSettings);
+ method public androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption? findComplicationSlotsOptionForUserStyle(androidx.wear.watchface.style.UserStyle userStyle);
method public operator androidx.wear.watchface.style.UserStyleSetting? get(androidx.wear.watchface.style.UserStyleSetting.Id settingId);
method public byte[] getDigestHash();
method public java.util.List<androidx.wear.watchface.style.UserStyleSetting> getRootUserStyleSettings();
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
index 24ce1c1..1d98429 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/CurrentUserStyleRepository.kt
@@ -19,9 +19,11 @@
import android.content.res.Resources
import android.content.res.XmlResourceParser
import android.graphics.drawable.Icon
+import android.os.Build
import androidx.annotation.RestrictTo
import androidx.wear.watchface.complications.IllegalNodeException
import androidx.wear.watchface.complications.iterate
+import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
import androidx.wear.watchface.style.UserStyleSetting.Option
import androidx.wear.watchface.style.data.UserStyleSchemaWireFormat
import androidx.wear.watchface.style.data.UserStyleWireFormat
@@ -422,8 +424,10 @@
*
* @param userStyleSettings The user configurable style categories associated with this watch face.
* Empty if the watch face doesn't support user styling. Note we allow at most one
- * [UserStyleSetting.ComplicationSlotsUserStyleSetting] and one
- * [UserStyleSetting.CustomValueUserStyleSetting] in the list.
+ * [UserStyleSetting.CustomValueUserStyleSetting] in the list. Prior to android T ot most one
+ * [UserStyleSetting.ComplicationSlotsUserStyleSetting] is allowed, however from android T it's
+ * possible with hierarchical styles for there to be more than one, but at most one can be active at
+ * any given time.
*/
public class UserStyleSchema constructor(
userStyleSettings: List<UserStyleSetting>
@@ -521,9 +525,12 @@
}
}
- // This requirement makes it easier to implement companion editors.
- require(complicationSlotsUserStyleSettingCount <= 1) {
- "At most only one ComplicationSlotsUserStyleSetting is allowed"
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ validateComplicationSettings(rootUserStyleSettings, null)
+ } else {
+ require(complicationSlotsUserStyleSettingCount <= 1) {
+ "Prior to Android T, at most only one ComplicationSlotsUserStyleSetting is allowed"
+ }
}
// There's a hard limit to how big Schema + UserStyle can be and since this data is sent
@@ -535,6 +542,28 @@
}
}
+ private fun validateComplicationSettings(
+ settings: Collection<UserStyleSetting>,
+ initialPrevSetting: UserStyleSetting.ComplicationSlotsUserStyleSetting?
+ ) {
+ var prevSetting = initialPrevSetting
+ for (setting in settings) {
+ if (setting is UserStyleSetting.ComplicationSlotsUserStyleSetting) {
+ require(prevSetting == null) {
+ "From Android T multiple ComplicationSlotsUserStyleSettings are allowed, but" +
+ " at most one can be active for any permutation of UserStyle. Note: " +
+ "$setting and $prevSetting"
+ }
+ prevSetting = setting
+ }
+ }
+ for (setting in settings) {
+ for (option in setting.options) {
+ validateComplicationSettings(option.childSettings, prevSetting)
+ }
+ }
+ }
+
/** @hide */
@Suppress("Deprecation") // userStyleSettings
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -638,6 +667,40 @@
private class NullOutputStream : OutputStream() {
override fun write(value: Int) {}
}
+
+ private fun findActiveComplicationSetting(
+ settings: Collection<UserStyleSetting>,
+ userStyle: UserStyle
+ ): UserStyleSetting.ComplicationSlotsUserStyleSetting? {
+ for (setting in settings) {
+ if (setting is UserStyleSetting.ComplicationSlotsUserStyleSetting) {
+ return setting
+ }
+ findActiveComplicationSetting(userStyle[setting]!!.childSettings, userStyle)?.let {
+ return it
+ }
+ }
+ return null
+ }
+
+ /**
+ * At most one [UserStyleSetting.ComplicationSlotsUserStyleSetting] can be active at a time
+ * based on the hierarchy of styles for any given [UserStyle]. This function finds the current
+ * active [UserStyleSetting.ComplicationSlotsUserStyleSetting] based upon the [userStyle] and,
+ * if there is one, it returns the corresponding selected [ComplicationSlotsOption]. Otherwise
+ * it returns `null`.
+ *
+ * @param userStyle The [UserStyle] for which the function will search for the selected
+ * [ComplicationSlotsOption], if any.
+ * @return The selected [ComplicationSlotsOption] for the [userStyle] if any, or `null`
+ * otherwise.
+ */
+ public fun findComplicationSlotsOptionForUserStyle(
+ userStyle: UserStyle
+ ): ComplicationSlotsOption? =
+ findActiveComplicationSetting(rootUserStyleSettings, userStyle)?.let {
+ userStyle[it] as ComplicationSlotsOption
+ }
}
/**
diff --git a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
index 6da2687..4691492 100644
--- a/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
+++ b/wear/watchface/watchface-style/src/main/java/androidx/wear/watchface/style/UserStyleSetting.kt
@@ -878,8 +878,11 @@
* The ComplicationsManager listens for style changes with this setting and when a
* [ComplicationSlotsOption] is selected the overrides are automatically applied. Note its
* suggested that the default [ComplicationSlotOverlay] (the first entry in the list) does
- * not apply any overrides. Only a single [ComplicationSlotsUserStyleSetting] is permitted in
- * the [UserStyleSchema].
+ * not apply any overrides.
+ *
+ * From android T multiple [ComplicationSlotsUserStyleSetting] are allowed in a style hierarchy
+ * as long as at most one is active for any permutation of [UserStyle]. Prior to android T only
+ * a single ComplicationSlotsUserStyleSetting was allowed.
*
* Not to be confused with complication data source selection.
*/
diff --git a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
index 6b96dcf..8afe47f 100644
--- a/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
+++ b/wear/watchface/watchface-style/src/test/java/androidx/wear/watchface/style/CurrentUserStyleRepositoryTest.kt
@@ -20,6 +20,9 @@
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.wear.watchface.style.UserStyleSetting.BooleanUserStyleSetting
+import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting
+import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
+import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay
import androidx.wear.watchface.style.UserStyleSetting.CustomValueUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.CustomValueUserStyleSetting.CustomValueOption
import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting
@@ -32,6 +35,7 @@
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
+import org.robolectric.annotation.Config
private val redStyleOption =
ListUserStyleSetting.ListOption(Option.Id("red_style"), "Red", icon = null)
@@ -121,6 +125,7 @@
private val optionTrue = BooleanUserStyleSetting.BooleanOption.TRUE
private val optionFalse = BooleanUserStyleSetting.BooleanOption.FALSE
+@Config(minSdk = Build.VERSION_CODES.TIRAMISU)
@RunWith(StyleTestRunner::class)
class CurrentUserStyleRepositoryTest {
@@ -736,6 +741,307 @@
assertThat(watchHandLengthStyleSetting.hasParent).isTrue()
assertThat(watchHandStyleSetting.hasParent).isTrue()
}
+
+ @Test
+ fun invalid_multiple_ComplicationSlotsUserStyleSettings_same_level() {
+ val leftAndRightComplications = ComplicationSlotsOption(
+ Option.Id("LEFT_AND_RIGHT_COMPLICATIONS"),
+ displayName = "Both",
+ icon = null,
+ emptyList()
+ )
+ val complicationSetting1 = ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("complications_style_setting1"),
+ displayName = "Complications",
+ description = "Number and position",
+ icon = null,
+ complicationConfig = listOf(leftAndRightComplications),
+ listOf(WatchFaceLayer.COMPLICATIONS)
+ )
+ val complicationSetting2 = ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("complications_style_setting2"),
+ displayName = "Complications",
+ description = "Number and position",
+ icon = null,
+ complicationConfig = listOf(leftAndRightComplications),
+ listOf(WatchFaceLayer.COMPLICATIONS)
+ )
+ val optionA1 = ListUserStyleSetting.ListOption(
+ Option.Id("a1_style"),
+ displayName = "A1",
+ icon = null,
+ childSettings = listOf(complicationSetting1, complicationSetting2)
+ )
+ val optionA2 = ListUserStyleSetting.ListOption(
+ Option.Id("a2_style"),
+ displayName = "A2",
+ icon = null,
+ childSettings = listOf(complicationSetting2)
+ )
+
+ assertThrows(IllegalArgumentException::class.java) {
+ UserStyleSchema(
+ listOf(
+ ListUserStyleSetting(
+ UserStyleSetting.Id("a123"),
+ displayName = "A123",
+ description = "A123",
+ icon = null,
+ listOf(optionA1, optionA2),
+ WatchFaceLayer.ALL_WATCH_FACE_LAYERS
+ ),
+ complicationSetting1,
+ complicationSetting2
+ )
+ )
+ }
+ }
+
+ @Test
+ fun invalid_multiple_ComplicationSlotsUserStyleSettings_different_levels() {
+ val leftAndRightComplications = ComplicationSlotsOption(
+ Option.Id("LEFT_AND_RIGHT_COMPLICATIONS"),
+ displayName = "Both",
+ icon = null,
+ emptyList()
+ )
+ val complicationSetting1 = ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("complications_style_setting1"),
+ displayName = "Complications",
+ description = "Number and position",
+ icon = null,
+ complicationConfig = listOf(leftAndRightComplications),
+ listOf(WatchFaceLayer.COMPLICATIONS)
+ )
+ val complicationSetting2 = ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("complications_style_setting2"),
+ displayName = "Complications",
+ description = "Number and position",
+ icon = null,
+ complicationConfig = listOf(leftAndRightComplications),
+ listOf(WatchFaceLayer.COMPLICATIONS)
+ )
+ val optionA1 = ListUserStyleSetting.ListOption(
+ Option.Id("a1_style"),
+ displayName = "A1",
+ icon = null,
+ childSettings = listOf(complicationSetting1)
+ )
+ val optionA2 = ListUserStyleSetting.ListOption(
+ Option.Id("a2_style"),
+ displayName = "A2",
+ icon = null
+ )
+
+ assertThrows(IllegalArgumentException::class.java) {
+ UserStyleSchema(
+ listOf(
+ ListUserStyleSetting(
+ UserStyleSetting.Id("a123"),
+ displayName = "A123",
+ description = "A123",
+ icon = null,
+ listOf(optionA1, optionA2),
+ WatchFaceLayer.ALL_WATCH_FACE_LAYERS
+ ),
+ complicationSetting1,
+ complicationSetting2
+ )
+ )
+ }
+ }
+
+ @Test
+ @Suppress("deprecation")
+ fun multiple_ComplicationSlotsUserStyleSettings() {
+ // The code below constructs the following hierarchy:
+ //
+ // rootABChoice
+ // rootOptionA ---------/ \--------- rootOptionB
+ // | |
+ // a123Choice b12Choice
+ // / | \ / \
+ // optionA1 optionA2 optionA3 optionB1 optionB2
+ // | | |
+ // | complicationSetting2 complicationSetting1
+ // complicationSetting1
+
+ val leftComplicationID = 101
+ val rightComplicationID = 102
+ val leftAndRightComplications = ComplicationSlotsOption(
+ Option.Id("LEFT_AND_RIGHT_COMPLICATIONS"),
+ displayName = "Both",
+ icon = null,
+ emptyList()
+ )
+ val noComplications = ComplicationSlotsOption(
+ Option.Id("NO_COMPLICATIONS"),
+ displayName = "None",
+ icon = null,
+ listOf(
+ ComplicationSlotOverlay(leftComplicationID, enabled = false),
+ ComplicationSlotOverlay(rightComplicationID, enabled = false)
+ )
+ )
+ val complicationSetting1 = ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("complications_style_setting"),
+ displayName = "Complications",
+ description = "Number and position",
+ icon = null,
+ complicationConfig = listOf(leftAndRightComplications, noComplications),
+ listOf(WatchFaceLayer.COMPLICATIONS)
+ )
+
+ val leftComplication = ComplicationSlotsOption(
+ Option.Id("LEFT_COMPLICATION"),
+ displayName = "Left",
+ icon = null,
+ listOf(ComplicationSlotOverlay(rightComplicationID, enabled = false))
+ )
+ val rightComplication = ComplicationSlotsOption(
+ Option.Id("RIGHT_COMPLICATION"),
+ displayName = "Right",
+ icon = null,
+ listOf(ComplicationSlotOverlay(leftComplicationID, enabled = false))
+ )
+ val complicationSetting2 = ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("complications_style_setting2"),
+ displayName = "Complications",
+ description = "Number and position",
+ icon = null,
+ complicationConfig = listOf(leftComplication, rightComplication),
+ listOf(WatchFaceLayer.COMPLICATIONS)
+ )
+
+ val normal = ComplicationSlotsOption(
+ Option.Id("Normal"),
+ displayName = "Normal",
+ icon = null,
+ emptyList()
+ )
+ val traversal = ComplicationSlotsOption(
+ Option.Id("Traversal"),
+ displayName = "Traversal",
+ icon = null,
+ listOf(
+ ComplicationSlotOverlay(leftComplicationID, accessibilityTraversalIndex = 3),
+ ComplicationSlotOverlay(rightComplicationID, accessibilityTraversalIndex = 2)
+ )
+ )
+ val complicationSetting3 = ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("complications_style_setting3"),
+ displayName = "Traversal Order",
+ description = "Traversal Order",
+ icon = null,
+ complicationConfig = listOf(normal, traversal),
+ listOf(WatchFaceLayer.COMPLICATIONS)
+ )
+
+ val optionA1 = ListUserStyleSetting.ListOption(
+ Option.Id("a1_style"),
+ displayName = "A1",
+ icon = null,
+ childSettings = listOf(complicationSetting1)
+ )
+ val optionA2 = ListUserStyleSetting.ListOption(
+ Option.Id("a2_style"),
+ displayName = "A2",
+ icon = null,
+ childSettings = listOf(complicationSetting2)
+ )
+ val optionA3 =
+ ListUserStyleSetting.ListOption(Option.Id("a3_style"), "A3", icon = null)
+
+ val a123Choice = ListUserStyleSetting(
+ UserStyleSetting.Id("a123"),
+ displayName = "A123",
+ description = "A123",
+ icon = null,
+ listOf(optionA1, optionA2, optionA3),
+ WatchFaceLayer.ALL_WATCH_FACE_LAYERS
+ )
+
+ val optionB1 = ListUserStyleSetting.ListOption(
+ Option.Id("b1_style"),
+ displayName = "B1",
+ icon = null,
+ childSettings = listOf(complicationSetting3)
+ )
+ val optionB2 =
+ ListUserStyleSetting.ListOption(Option.Id("b2_style"), "B2", icon = null)
+
+ val b12Choice = ListUserStyleSetting(
+ UserStyleSetting.Id("b12"),
+ displayName = "B12",
+ "B12",
+ icon = null,
+ listOf(optionB1, optionB2),
+ WatchFaceLayer.ALL_WATCH_FACE_LAYERS
+ )
+
+ val rootOptionA = ListUserStyleSetting.ListOption(
+ Option.Id("a_style"),
+ displayName = "A",
+ icon = null,
+ childSettings = listOf(a123Choice)
+ )
+ val rootOptionB = ListUserStyleSetting.ListOption(
+ Option.Id("b_style"),
+ displayName = "B",
+ icon = null,
+ childSettings = listOf(b12Choice)
+ )
+
+ val rootABChoice = ListUserStyleSetting(
+ UserStyleSetting.Id("root_ab"),
+ displayName = "AB",
+ description = "AB",
+ icon = null,
+ listOf(rootOptionA, rootOptionB),
+ WatchFaceLayer.ALL_WATCH_FACE_LAYERS
+ )
+
+ val schema = UserStyleSchema(
+ listOf(
+ rootABChoice,
+ a123Choice,
+ b12Choice,
+ complicationSetting1,
+ complicationSetting2,
+ complicationSetting3
+ )
+ )
+
+ val userStyleMap = mutableMapOf(
+ rootABChoice to rootOptionA,
+ a123Choice to optionA1,
+ b12Choice to optionB1,
+ complicationSetting1 to leftAndRightComplications,
+ complicationSetting2 to rightComplication,
+ complicationSetting3 to traversal
+ )
+
+ // Test various userStyleMap permutations to ensure the correct ComplicationSlotsOption is
+ // returned.
+ assertThat(schema.findComplicationSlotsOptionForUserStyle(UserStyle(userStyleMap)))
+ .isEqualTo(leftAndRightComplications)
+
+ userStyleMap[a123Choice] = optionA2
+ assertThat(schema.findComplicationSlotsOptionForUserStyle(UserStyle(userStyleMap)))
+ .isEqualTo(rightComplication)
+
+ userStyleMap[a123Choice] = optionA3
+ assertThat(schema.findComplicationSlotsOptionForUserStyle(UserStyle(userStyleMap)))
+ .isNull()
+
+ userStyleMap[rootABChoice] = rootOptionB
+ assertThat(schema.findComplicationSlotsOptionForUserStyle(UserStyle(userStyleMap)))
+ .isEqualTo(traversal)
+
+ userStyleMap[b12Choice] = optionB2
+ assertThat(schema.findComplicationSlotsOptionForUserStyle(UserStyle(userStyleMap)))
+ .isNull()
+ }
}
@RunWith(StyleTestRunner::class)
diff --git a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleHierarchicalStyleWatchFaceService.kt b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleHierarchicalStyleWatchFaceService.kt
index 45a6c9b3..1ae8d16 100644
--- a/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleHierarchicalStyleWatchFaceService.kt
+++ b/wear/watchface/watchface/samples/src/main/java/androidx/wear/watchface/samples/ExampleHierarchicalStyleWatchFaceService.kt
@@ -16,14 +16,18 @@
package androidx.wear.watchface.samples
+import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
+import android.graphics.RectF
import android.graphics.drawable.Icon
import android.view.SurfaceHolder
import androidx.annotation.Px
+import androidx.wear.watchface.CanvasComplicationFactory
import androidx.wear.watchface.CanvasType
+import androidx.wear.watchface.ComplicationSlot
import androidx.wear.watchface.ComplicationSlotsManager
import androidx.wear.watchface.DrawMode
import androidx.wear.watchface.RenderParameters
@@ -32,18 +36,24 @@
import androidx.wear.watchface.WatchFaceService
import androidx.wear.watchface.WatchFaceType
import androidx.wear.watchface.WatchState
+import androidx.wear.watchface.complications.ComplicationSlotBounds
+import androidx.wear.watchface.complications.DefaultComplicationDataSourcePolicy
+import androidx.wear.watchface.complications.SystemDataSources
+import androidx.wear.watchface.complications.data.ComplicationType
+import androidx.wear.watchface.complications.rendering.CanvasComplicationDrawable
import androidx.wear.watchface.style.CurrentUserStyleRepository
import androidx.wear.watchface.style.UserStyleSchema
import androidx.wear.watchface.style.UserStyleSetting
-import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting
-import androidx.wear.watchface.style.UserStyleSetting.DoubleRangeUserStyleSetting.DoubleRangeOption
+import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting
+import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotOverlay
import androidx.wear.watchface.style.UserStyleSetting.ListUserStyleSetting.ListOption
-import androidx.wear.watchface.style.UserStyleSetting.LongRangeUserStyleSetting
-import androidx.wear.watchface.style.UserStyleSetting.LongRangeUserStyleSetting.LongRangeOption
import androidx.wear.watchface.style.WatchFaceLayer
import java.time.ZonedDateTime
import kotlin.math.cos
import kotlin.math.sin
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
open class ExampleHierarchicalStyleWatchFaceService : WatchFaceService() {
@@ -52,7 +62,7 @@
UserStyleSetting.Option.Id("12_style"),
resources,
R.string.digital_clock_style_12,
- icon = null
+ Icon.createWithResource(this, R.drawable.red_style)
)
}
@@ -61,7 +71,48 @@
UserStyleSetting.Option.Id("24_style"),
resources,
R.string.digital_clock_style_24,
- icon = null
+ Icon.createWithResource(this, R.drawable.red_style)
+ )
+ }
+
+ @Suppress("Deprecation")
+ private val digitalComplicationSettings by lazy {
+ ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("DigitalComplications"),
+ resources,
+ R.string.digital_complications_setting,
+ R.string.digital_complications_setting_description,
+ icon = null,
+ complicationConfig = listOf(
+ ComplicationSlotsUserStyleSetting.ComplicationSlotsOption(
+ UserStyleSetting.Option.Id("On"),
+ resources,
+ R.string.digital_complication_on_screen_name,
+ Icon.createWithResource(this, R.drawable.on),
+ listOf(
+ ComplicationSlotOverlay(
+ COMPLICATION1_ID,
+ enabled = true,
+ complicationSlotBounds =
+ ComplicationSlotBounds(RectF(0.1f, 0.4f, 0.3f, 0.6f))
+ ),
+ ComplicationSlotOverlay(COMPLICATION2_ID, enabled = false),
+ ComplicationSlotOverlay(COMPLICATION3_ID, enabled = false)
+ ),
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotsOption(
+ UserStyleSetting.Option.Id("Off"),
+ resources,
+ R.string.digital_complication_off_screen_name,
+ Icon.createWithResource(this, R.drawable.off),
+ listOf(
+ ComplicationSlotOverlay(COMPLICATION1_ID, enabled = false),
+ ComplicationSlotOverlay(COMPLICATION2_ID, enabled = false),
+ ComplicationSlotOverlay(COMPLICATION3_ID, enabled = false)
+ )
+ )
+ ),
+ listOf(WatchFaceLayer.COMPLICATIONS)
)
}
@@ -120,31 +171,63 @@
)
}
- internal val watchHandLengthStyleSetting by lazy {
- DoubleRangeUserStyleSetting(
- UserStyleSetting.Id(WATCH_HAND_LENGTH_STYLE_SETTING),
+ internal val drawHoursSetting by lazy {
+ UserStyleSetting.BooleanUserStyleSetting(
+ UserStyleSetting.Id(HOURS_STYLE_SETTING),
resources,
- R.string.watchface_hand_length_setting,
- R.string.watchface_hand_length_setting_description,
- null,
- 0.25,
- 1.0,
- listOf(WatchFaceLayer.COMPLICATIONS_OVERLAY),
- 0.75
+ R.string.watchface_draw_hours_setting,
+ R.string.watchface_draw_hours_setting_description,
+ icon = null,
+ listOf(WatchFaceLayer.BASE),
+ defaultValue = true,
+ watchFaceEditorData = null
)
}
- internal val hoursDrawFreqStyleSetting by lazy {
- LongRangeUserStyleSetting(
- UserStyleSetting.Id(HOURS_DRAW_FREQ_STYLE_SETTING),
+ @Suppress("Deprecation")
+ private val analogComplicationSettings by lazy {
+ ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("AnalogComplications"),
resources,
- R.string.watchface_draw_hours_freq_setting,
- R.string.watchface_draw_hours_freq_setting_description,
- null,
- HOURS_DRAW_FREQ_MIN,
- HOURS_DRAW_FREQ_MAX,
- listOf(WatchFaceLayer.BASE),
- HOURS_DRAW_FREQ_DEFAULT
+ R.string.watchface_complications_setting,
+ R.string.watchface_complications_setting_description,
+ icon = null,
+ complicationConfig = listOf(
+ ComplicationSlotsUserStyleSetting.ComplicationSlotsOption(
+ UserStyleSetting.Option.Id("One"),
+ resources,
+ R.string.analog_complication_one_screen_name,
+ Icon.createWithResource(this, R.drawable.one),
+ listOf(
+ ComplicationSlotOverlay(COMPLICATION1_ID, enabled = true),
+ ComplicationSlotOverlay(COMPLICATION2_ID, enabled = false),
+ ComplicationSlotOverlay(COMPLICATION3_ID, enabled = false)
+ )
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotsOption(
+ UserStyleSetting.Option.Id("Two"),
+ resources,
+ R.string.analog_complication_two_screen_name,
+ Icon.createWithResource(this, R.drawable.two),
+ listOf(
+ ComplicationSlotOverlay(COMPLICATION1_ID, enabled = true),
+ ComplicationSlotOverlay(COMPLICATION2_ID, enabled = true),
+ ComplicationSlotOverlay(COMPLICATION3_ID, enabled = false)
+ )
+ ),
+ ComplicationSlotsUserStyleSetting.ComplicationSlotsOption(
+ UserStyleSetting.Option.Id("Three"),
+ resources,
+ R.string.analog_complication_three_screen_name,
+ Icon.createWithResource(this, R.drawable.three),
+ listOf(
+ ComplicationSlotOverlay(COMPLICATION1_ID, enabled = true),
+ ComplicationSlotOverlay(COMPLICATION2_ID, enabled = true),
+ ComplicationSlotOverlay(COMPLICATION3_ID, enabled = true)
+ )
+ )
+ ),
+ listOf(WatchFaceLayer.COMPLICATIONS)
)
}
@@ -153,8 +236,12 @@
UserStyleSetting.Option.Id("digital"),
resources,
R.string.style_digital_watch,
- icon = null,
- childSettings = listOf(digitalClockStyleSetting, colorStyleSetting)
+ icon = Icon.createWithResource(this, R.drawable.d),
+ childSettings = listOf(
+ digitalClockStyleSetting,
+ colorStyleSetting,
+ digitalComplicationSettings
+ )
)
}
@@ -163,8 +250,12 @@
UserStyleSetting.Option.Id("analog"),
resources,
R.string.style_analog_watch,
- icon = null,
- childSettings = listOf(watchHandLengthStyleSetting, hoursDrawFreqStyleSetting)
+ icon = Icon.createWithResource(this, R.drawable.a),
+ childSettings = listOf(
+ colorStyleSetting,
+ drawHoursSetting,
+ analogComplicationSettings
+ )
)
}
@@ -185,11 +276,97 @@
watchFaceType,
digitalClockStyleSetting,
colorStyleSetting,
- watchHandLengthStyleSetting,
- hoursDrawFreqStyleSetting
+ drawHoursSetting,
+ digitalComplicationSettings,
+ analogComplicationSettings
)
)
+ private val watchFaceStyle by lazy {
+ WatchFaceColorStyle.create(this, "red_style")
+ }
+
+ public override fun createComplicationSlotsManager(
+ currentUserStyleRepository: CurrentUserStyleRepository
+ ): ComplicationSlotsManager {
+ val canvasComplicationFactory =
+ CanvasComplicationFactory { watchState, listener ->
+ CanvasComplicationDrawable(
+ watchFaceStyle.getDrawable(this@ExampleHierarchicalStyleWatchFaceService)!!,
+ watchState,
+ listener
+ )
+ }
+
+ val complicationOne = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ COMPLICATION1_ID,
+ canvasComplicationFactory,
+ listOf(
+ ComplicationType.RANGED_VALUE,
+ ComplicationType.GOAL_PROGRESS,
+ ComplicationType.WEIGHTED_ELEMENTS,
+ ComplicationType.SHORT_TEXT,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_WATCH_BATTERY,
+ ComplicationType.RANGED_VALUE
+ ),
+ ComplicationSlotBounds(RectF(0.6f, 0.1f, 0.8f, 0.3f))
+ ).setNameResourceId(R.string.hierarchical_complication1_screen_name)
+ .setScreenReaderNameResourceId(
+ R.string.hierarchical_complication1_screen_reader_name
+ ).build()
+
+ val complicationTwo = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ COMPLICATION2_ID,
+ canvasComplicationFactory,
+ listOf(
+ ComplicationType.RANGED_VALUE,
+ ComplicationType.GOAL_PROGRESS,
+ ComplicationType.WEIGHTED_ELEMENTS,
+ ComplicationType.SHORT_TEXT,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_TIME_AND_DATE,
+ ComplicationType.SHORT_TEXT
+ ),
+ ComplicationSlotBounds(RectF(0.6f, 0.4f, 0.8f, 0.6f))
+ ).setNameResourceId(R.string.hierarchical_complication2_screen_name)
+ .setScreenReaderNameResourceId(
+ R.string.hierarchical_complication2_screen_reader_name
+ ).build()
+
+ val complicationThree = ComplicationSlot.createRoundRectComplicationSlotBuilder(
+ COMPLICATION3_ID,
+ canvasComplicationFactory,
+ listOf(
+ ComplicationType.RANGED_VALUE,
+ ComplicationType.GOAL_PROGRESS,
+ ComplicationType.WEIGHTED_ELEMENTS,
+ ComplicationType.SHORT_TEXT,
+ ComplicationType.MONOCHROMATIC_IMAGE,
+ ComplicationType.SMALL_IMAGE
+ ),
+ DefaultComplicationDataSourcePolicy(
+ SystemDataSources.DATA_SOURCE_SUNRISE_SUNSET,
+ ComplicationType.SHORT_TEXT
+ ),
+ ComplicationSlotBounds(RectF(0.6f, 0.7f, 0.8f, 0.9f))
+ ).setNameResourceId(R.string.hierarchical_complication3_screen_name)
+ .setScreenReaderNameResourceId(
+ R.string.hierarchical_complication3_screen_reader_name
+ ).build()
+
+ return ComplicationSlotsManager(
+ listOf(complicationOne, complicationTwo, complicationThree),
+ currentUserStyleRepository
+ )
+ }
+
override suspend fun createWatchFace(
surfaceHolder: SurfaceHolder,
watchState: WatchState,
@@ -206,6 +383,32 @@
16L
) {
val renderer = ExampleHierarchicalStyleWatchFaceRenderer()
+ val context: Context = this@ExampleHierarchicalStyleWatchFaceService
+
+ init {
+ CoroutineScope(Dispatchers.Main.immediate).launch {
+ currentUserStyleRepository.userStyle.collect { userStyle ->
+ for ((_, complication) in complicationSlotsManager.complicationSlots) {
+ (complication.renderer as CanvasComplicationDrawable).drawable =
+ when (userStyle[colorStyleSetting]) {
+ redStyle ->
+ WatchFaceColorStyle.create(context, "red_style")
+ .getDrawable(context)!!
+ greenStyle ->
+ WatchFaceColorStyle.create(context, "green_style")
+ .getDrawable(context)!!
+ blueStyle ->
+ WatchFaceColorStyle.create(context, "blue_style")
+ .getDrawable(context)!!
+ else -> throw IllegalArgumentException(
+ "Unrecognized colorStyleSetting "
+ )
+ }
+ }
+ }
+ }
+ }
+
override fun render(canvas: Canvas, bounds: Rect, zonedDateTime: ZonedDateTime) {
val currentStyle = currentUserStyleRepository.userStyle.value
when (currentStyle[watchFaceType]) {
@@ -232,10 +435,8 @@
bounds,
zonedDateTime,
renderParameters,
- (currentStyle[hoursDrawFreqStyleSetting]!!
- as LongRangeOption).value.toInt(),
- (currentStyle[watchHandLengthStyleSetting]!!
- as DoubleRangeOption).value.toFloat()
+ (currentStyle[drawHoursSetting]!!
+ as UserStyleSetting.BooleanUserStyleSetting.BooleanOption).value,
)
else -> {
@@ -245,6 +446,12 @@
)
}
}
+
+ for ((_, complication) in complicationSlotsManager.complicationSlots) {
+ if (complication.enabled) {
+ complication.render(canvas, zonedDateTime, renderParameters)
+ }
+ }
}
override fun renderHighlightLayer(
@@ -252,7 +459,11 @@
bounds: Rect,
zonedDateTime: ZonedDateTime
) {
- // Nothing to do.
+ for ((_, complication) in complicationSlotsManager.complicationSlots) {
+ if (complication.enabled) {
+ complication.renderHighlightLayer(canvas, zonedDateTime, renderParameters)
+ }
+ }
}
}
)
@@ -352,8 +563,7 @@
bounds: Rect,
zonedDateTime: ZonedDateTime,
renderParameters: RenderParameters,
- hoursDrawFreq: Int,
- watchHandLength: Float
+ drawHourPips: Boolean
) {
val isActive = renderParameters.drawMode !== DrawMode.AMBIENT
@@ -362,8 +572,8 @@
paint.color = Color.WHITE
paint.textSize = 20.0f
- if (isActive) {
- for (i in 12 downTo 1 step hoursDrawFreq) {
+ if (isActive && drawHourPips) {
+ for (i in 12 downTo 1 step 3) {
val rot = i.toFloat() / 12.0f * 2.0f * Math.PI
val dx = sin(rot).toFloat() * NUMBER_RADIUS_FRACTION * bounds.width().toFloat()
val dy = -cos(rot).toFloat() * NUMBER_RADIUS_FRACTION * bounds.width().toFloat()
@@ -386,8 +596,8 @@
val hourRot = (hours + minutes / 60.0f + seconds / 3600.0f) / 12.0f * 360.0f
val minuteRot = (minutes + seconds / 60.0f) / 60.0f * 360.0f
- val hourXRadius = bounds.width() * watchHandLength * 0.35f
- val hourYRadius = bounds.height() * watchHandLength * 0.35f
+ val hourXRadius = bounds.width() * 0.3f
+ val hourYRadius = bounds.height() * 0.3f
paint.strokeWidth = if (isActive) 8f else 5f
canvas.drawLine(
@@ -398,8 +608,8 @@
paint
)
- val minuteXRadius = bounds.width() * watchHandLength * 0.499f
- val minuteYRadius = bounds.height() * watchHandLength * 0.499f
+ val minuteXRadius = bounds.width() * 0.4f
+ val minuteYRadius = bounds.height() * 0.4f
paint.strokeWidth = if (isActive) 4f else 2.5f
canvas.drawLine(
@@ -418,11 +628,7 @@
private const val GREEN_STYLE = "green_style"
private const val BLUE_STYLE = "blue_style"
- private const val WATCH_HAND_LENGTH_STYLE_SETTING = "watch_hand_length_style_setting"
- private const val HOURS_DRAW_FREQ_STYLE_SETTING = "hours_draw_freq_style_setting"
- private const val HOURS_DRAW_FREQ_MIN = 1L
- private const val HOURS_DRAW_FREQ_MAX = 4L
- private const val HOURS_DRAW_FREQ_DEFAULT = 3L
+ private const val HOURS_STYLE_SETTING = "hours_style_setting"
private const val NUMBER_RADIUS_FRACTION = 0.45f
private val timeText = charArrayOf('1', '0', ':', '0', '9')
@@ -439,5 +645,9 @@
@Px
private val TEXT_PADDING = 12
+
+ const val COMPLICATION1_ID = 101
+ const val COMPLICATION2_ID = 102
+ const val COMPLICATION3_ID = 103
}
}
diff --git a/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/a.webp b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/a.webp
new file mode 100644
index 0000000..c837655
--- /dev/null
+++ b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/a.webp
Binary files differ
diff --git a/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/d.webp b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/d.webp
new file mode 100644
index 0000000..f47294e
--- /dev/null
+++ b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/d.webp
Binary files differ
diff --git a/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/off.webp b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/off.webp
new file mode 100644
index 0000000..e2db0da
--- /dev/null
+++ b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/off.webp
Binary files differ
diff --git a/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/on.webp b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/on.webp
new file mode 100644
index 0000000..91e5649
--- /dev/null
+++ b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/on.webp
Binary files differ
diff --git a/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/one.webp b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/one.webp
new file mode 100644
index 0000000..8a3622b
--- /dev/null
+++ b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/one.webp
Binary files differ
diff --git a/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/three.webp b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/three.webp
new file mode 100644
index 0000000..0c5ffc1
--- /dev/null
+++ b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/three.webp
Binary files differ
diff --git a/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/two.webp b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/two.webp
new file mode 100644
index 0000000..ceaa89a
--- /dev/null
+++ b/wear/watchface/watchface/samples/src/main/res/drawable-nodpi/two.webp
Binary files differ
diff --git a/wear/watchface/watchface/samples/src/main/res/values/strings.xml b/wear/watchface/watchface/samples/src/main/res/values/strings.xml
index a5d1d53..de9835e 100644
--- a/wear/watchface/watchface/samples/src/main/res/values/strings.xml
+++ b/wear/watchface/watchface/samples/src/main/res/values/strings.xml
@@ -25,7 +25,7 @@
<string name="gl_background_init_watch_face_name"
translatable="false">Background Init Watchface</string>
<string name="hierarchical_watch_face_name"
- translatable="false">Example Hierarchical Watchface</string>
+ translatable="false">Hierarchical Watchface (Needs T)</string>
<!-- Name of watchface style [CHAR LIMIT=20] -->
<string name="red_style_name">Red Style</string>
@@ -116,6 +116,14 @@
hours labeling [CHAR LIMIT=20] -->
<string name="watchface_draw_hours_freq_setting_description">Labeling frequency</string>
+ <!-- Menu option to select a widget that lets us configure whether to show hour pips
+ [CHAR LIMIT=20] -->
+ <string name="watchface_draw_hours_setting">Hour pips</string>
+
+ <!-- Sub title for the menu option to select a widget that lets us configure whether to show
+ hour pips [CHAR LIMIT=20] -->
+ <string name="watchface_draw_hours_setting_description">Display on/off</string>
+
<!-- Name of the left complication for use visually in the companion editor. [CHAR LIMIT=20] -->
<string name="left_complication_screen_name">Left</string>
@@ -177,4 +185,55 @@
<!-- Sub title for the menu option to select an analog or digital clock [CHAR LIMIT=20] -->
<string name="clock_type_description">Select analog or digital</string>
+
+ <!-- A menu option to select a widget that lets us configure the Complications (a watch
+ making term) for the digital watch [CHAR LIMIT=20] -->
+ <string name="digital_complications_setting">Complication</string>
+
+ <!-- Sub title for the menu option to select a widget that lets us configure the Complications
+ (a watch making term) for the digital watch [CHAR LIMIT=20] -->
+ <string name="digital_complications_setting_description">On or off</string>
+
+ <!-- List entry, enabling the complication for the digital watch. [CHAR LIMIT=20] -->
+ <string name="digital_complication_on_screen_name">On</string>
+
+ <!-- List entry, disabling the complication for the digital watch. [CHAR LIMIT=20] -->
+ <string name="digital_complication_off_screen_name">Off</string>
+
+ <!-- List entry setting the number of complications enabled for the analog watch.
+ [CHAR LIMIT=20] -->
+ <string name="analog_complication_one_screen_name">One</string>
+
+ <!-- List entry setting the number of complications enabled for the analog watch.
+ [CHAR LIMIT=20] -->
+ <string name="analog_complication_two_screen_name">Two</string>
+
+ <!-- List entry setting the number of complications enabled for the analog watch.
+ [CHAR LIMIT=20] -->
+ <string name="analog_complication_three_screen_name">Three</string>
+
+ <!-- Name of a complication at the top of the screen, for use visually in the companion editor.
+ [CHAR LIMIT=20] -->
+ <string name="hierarchical_complication1_screen_name">Top</string>
+
+ <!-- Name of a complication at the top of the screen, for use by the companion editor screen
+ reader. -->
+ <string name="hierarchical_complication1_screen_reader_name">Top complication</string>
+
+ <!-- Name of a complication at the middle of the screen, for use visually in the companion editor.
+ [CHAR LIMIT=20] -->
+ <string name="hierarchical_complication2_screen_name">Middle</string>
+
+ <!-- Name of a complication at the middle of the screen, for use by the companion editor screen
+ reader. -->
+ <string name="hierarchical_complication2_screen_reader_name">Middle complication</string>
+
+ <!-- Name of a complication at the bottom of the screen, for use visually in the companion editor.
+ [CHAR LIMIT=20] -->
+ <string name="hierarchical_complication3_screen_name">Bottom</string>
+
+ <!-- Name of a complication at the bottom of the screen, for use by the companion editor screen
+ reader. -->
+ <string name="hierarchical_complication3_screen_reader_name">Bottom complication</string>
+
</resources>
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt
index 4ad2be0..b422068 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/ComplicationSlotsManager.kt
@@ -42,7 +42,6 @@
import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting
import androidx.wear.watchface.style.UserStyleSetting.ComplicationSlotsUserStyleSetting.ComplicationSlotsOption
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import java.time.Instant
@@ -152,31 +151,47 @@
}
}
+ private fun applyInitialComplicationConfig() {
+ for ((id, complication) in complicationSlots) {
+ val initialConfig = initialComplicationConfigs[id]!!
+ complication.complicationSlotBounds = initialConfig.complicationSlotBounds
+ complication.enabled = initialConfig.enabled
+ complication.accessibilityTraversalIndex = initialConfig.accessibilityTraversalIndex
+ complication.nameResourceId = initialConfig.nameResourceId
+ complication.screenReaderNameResourceId = initialConfig.screenReaderNameResourceId
+ }
+ onComplicationsUpdated()
+ }
+
/** @hide */
@WorkerThread
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- @Suppress("Deprecation") // userStyleSettings
public fun listenForStyleChanges(coroutineScope: CoroutineScope) {
- val complicationsStyleCategory =
- currentUserStyleRepository.schema.userStyleSettings.firstOrNull {
- it is ComplicationSlotsUserStyleSetting
- } ?: return
-
- var previousOption: ComplicationSlotsOption = currentUserStyleRepository.userStyle.value[
- complicationsStyleCategory
- ]!! as ComplicationSlotsOption
+ var previousOption =
+ currentUserStyleRepository.schema.findComplicationSlotsOptionForUserStyle(
+ currentUserStyleRepository.userStyle.value
+ )
// Apply the initial settings on the worker thread.
- applyComplicationSlotsStyleCategoryOption(previousOption)
+ previousOption?.let {
+ applyComplicationSlotsStyleCategoryOption(it)
+ }
// Add a listener so we can track changes and automatically apply them on the UIThread
coroutineScope.launch {
- currentUserStyleRepository.userStyle.collect { userStyle ->
+ currentUserStyleRepository.userStyle.collect {
val newlySelectedOption =
- userStyle[complicationsStyleCategory]!! as ComplicationSlotsOption
+ currentUserStyleRepository.schema.findComplicationSlotsOptionForUserStyle(
+ currentUserStyleRepository.userStyle.value
+ )
+
if (previousOption != newlySelectedOption) {
previousOption = newlySelectedOption
- applyComplicationSlotsStyleCategoryOption(newlySelectedOption)
+ if (newlySelectedOption == null) {
+ applyInitialComplicationConfig()
+ } else {
+ applyComplicationSlotsStyleCategoryOption(newlySelectedOption)
+ }
}
}
}
diff --git a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
index 92c05ee..1987eb6 100644
--- a/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
+++ b/wear/watchface/watchface/src/main/java/androidx/wear/watchface/WatchFaceService.kt
@@ -2489,7 +2489,7 @@
"The estimated wire size of the supplied UserStyleSchemas for watch face " +
"$packageName is too big at $estimatedBytes bytes. UserStyleSchemas get sent " +
"to the companion over bluetooth and should be as small as possible for this " +
- "to be performant."
+ "to be performant. The maximum size is " + MAX_REASONABLE_SCHEMA_WIRE_SIZE_BYTES
}
}
diff --git a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
index 0bdb170..97ca016 100644
--- a/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
+++ b/wear/watchface/watchface/src/test/java/androidx/wear/watchface/WatchFaceServiceTest.kt
@@ -409,6 +409,17 @@
),
affectsWatchFaceLayers = listOf(WatchFaceLayer.COMPLICATIONS)
)
+ private val complicationsStyleSetting2 = ComplicationSlotsUserStyleSetting(
+ UserStyleSetting.Id("complications_style_setting2"),
+ "AllComplicationSlots",
+ "Number and position",
+ icon = null,
+ complicationConfig = listOf(
+ leftOnlyComplicationsOption,
+ rightOnlyComplicationsOption
+ ),
+ affectsWatchFaceLayers = listOf(WatchFaceLayer.COMPLICATIONS)
+ )
private lateinit var renderer: TestRenderer
private lateinit var complicationSlotsManager: ComplicationSlotsManager
@@ -2701,6 +2712,79 @@
}
@Test
+ fun hierarchical_complicationsStyleSetting() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ return
+ }
+
+ val option1 = ListUserStyleSetting.ListOption(
+ Option.Id("1"),
+ displayName = "1",
+ icon = null,
+ childSettings = listOf(complicationsStyleSetting)
+ )
+ val option2 = ListUserStyleSetting.ListOption(
+ Option.Id("2"),
+ displayName = "2",
+ icon = null,
+ childSettings = listOf(complicationsStyleSetting2)
+ )
+ val option3 = ListUserStyleSetting.ListOption(Option.Id("3"), "3", icon = null)
+ val choice = ListUserStyleSetting(
+ UserStyleSetting.Id("123"),
+ displayName = "123",
+ description = "123",
+ icon = null,
+ listOf(option1, option2, option3),
+ WatchFaceLayer.ALL_WATCH_FACE_LAYERS
+ )
+
+ initEngine(
+ WatchFaceType.DIGITAL,
+ listOf(leftComplication, rightComplication),
+ UserStyleSchema(listOf(choice, complicationsStyleSetting, complicationsStyleSetting2)),
+ apiVersion = 4
+ )
+
+ currentUserStyleRepository.updateUserStyle(
+ UserStyle(
+ mapOf(
+ choice to option1,
+ complicationsStyleSetting to noComplicationsOption, // Active
+ complicationsStyleSetting2 to rightOnlyComplicationsOption
+ )
+ )
+ )
+ assertFalse(leftComplication.enabled)
+ assertFalse(rightComplication.enabled)
+
+ currentUserStyleRepository.updateUserStyle(
+ UserStyle(
+ mapOf(
+ choice to option2,
+ complicationsStyleSetting to noComplicationsOption,
+ complicationsStyleSetting2 to rightOnlyComplicationsOption // Active
+ )
+ )
+ )
+ assertFalse(leftComplication.enabled)
+ assertTrue(rightComplication.enabled)
+
+ // By default all complications are active if no complicationsStyleSetting applies.
+ currentUserStyleRepository.updateUserStyle(
+ UserStyle(
+ mapOf(
+ choice to option3,
+ complicationsStyleSetting to noComplicationsOption,
+ complicationsStyleSetting2 to rightOnlyComplicationsOption
+ )
+ )
+ )
+ assertTrue(leftComplication.enabled)
+ assertTrue(rightComplication.enabled)
+ }
+
+ @Test
public fun observeComplicationData() {
initWallpaperInteractiveWatchFaceInstance(
WatchFaceType.ANALOG,