[go: nahoru, domu]

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,