Clock font axes confuguration content (2/2)

Test: See bug
Bug: 350718184
Flag: com.android.systemui.shared.new_customization_picker_ui
Change-Id: Ifb4ab33344fc62ea3c3e1a0d292ff7fa19cb8324
diff --git a/res/layout/clock_style_option2.xml b/res/layout/clock_style_option2.xml
new file mode 100644
index 0000000..8bb60d1
--- /dev/null
+++ b/res/layout/clock_style_option2.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!-- Content description is set programmatically on the parent FrameLayout -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="80dp"
+    android:layout_height="80dp"
+    android:clipToPadding="false"
+    android:clipChildren="false">
+
+    <com.android.wallpaper.picker.option.ui.view.OptionItemBackground
+        android:id="@id/background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no" />
+
+    <ImageView
+        android:id="@+id/foreground"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="@dimen/floating_sheet_clock_style_thumbnail_margin"
+        android:src="@drawable/ic_clock_24px" />
+
+    <ImageView
+        android:id="@+id/edit_icon"
+        android:layout_width="@dimen/floating_sheet_clock_edit_icon_size"
+        android:layout_height="@dimen/floating_sheet_clock_edit_icon_size"
+        android:layout_marginTop="-16dp"
+        android:layout_marginStart="50dp"
+        android:src="@drawable/edit_icon"
+        android:importantForAccessibility="no" />
+</FrameLayout>
+
diff --git a/res/layout/floating_sheet_clock.xml b/res/layout/floating_sheet_clock.xml
index ec2e6c1..cdaf3de 100644
--- a/res/layout/floating_sheet_clock.xml
+++ b/res/layout/floating_sheet_clock.xml
@@ -46,6 +46,7 @@
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintTop_toTopOf="parent"
                 app:layout_constraintBottom_toTopOf="@+id/clock_style_clock_size_title"
+                android:layout_marginBottom="8dp"
                 android:clipToPadding="false"
                 android:clipChildren="false">
 
@@ -64,10 +65,15 @@
                     android:layout_height="@dimen/floating_sheet_clock_style_option_size"
                     android:visibility="invisible" />
 
+                <!--
+                TODO (b/377528523): We intentionally disable over scroll mode since it will clip the
+                  edit icon when in over scroll.
+                -->
                 <androidx.recyclerview.widget.RecyclerView
                     android:id="@+id/clock_style_list"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:overScrollMode="never"
                     android:clipChildren="false"
                     android:clipToPadding="false"/>
             </FrameLayout>
@@ -167,7 +173,6 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:paddingHorizontal="@dimen/floating_sheet_content_horizontal_padding"
-            android:paddingVertical="@dimen/floating_sheet_content_vertical_padding"
             android:orientation="vertical"
             android:clipToPadding="false"
             android:clipChildren="false">
diff --git a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
index 2d53c0e..5f982e2 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
@@ -22,11 +22,13 @@
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.view.ViewTreeObserver.OnGlobalLayoutListener
+import android.widget.FrameLayout
 import android.widget.ImageView
+import android.widget.LinearLayout
 import android.widget.SeekBar
 import android.widget.Switch
 import android.widget.TextView
-import androidx.core.view.doOnLayout
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
@@ -53,12 +55,15 @@
 import com.android.wallpaper.picker.customization.ui.view.adapter.FloatingToolbarTabAdapter
 import com.android.wallpaper.picker.customization.ui.viewmodel.ColorUpdateViewModel
 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
+import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter2
 import java.lang.ref.WeakReference
 import kotlin.math.abs
+import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.launch
 
 object ClockFloatingSheetBinder {
@@ -68,8 +73,8 @@
 
     private val _clockFloatingSheetHeights: MutableStateFlow<ClockFloatingSheetHeightsViewModel?> =
         MutableStateFlow(null)
-    private val clockFloatingSheetHeights: Flow<ClockFloatingSheetHeightsViewModel?> =
-        _clockFloatingSheetHeights.asStateFlow()
+    private val clockFloatingSheetHeights: Flow<ClockFloatingSheetHeightsViewModel> =
+        _clockFloatingSheetHeights.asStateFlow().filterNotNull()
 
     fun bind(
         view: View,
@@ -90,7 +95,7 @@
                 .also { tabs.setAdapter(it) }
 
         val floatingSheetContainer =
-            view.requireViewById<ViewGroup>(R.id.clock_floating_sheet_content_container)
+            view.requireViewById<FrameLayout>(R.id.clock_floating_sheet_content_container)
 
         // Clock style
         val clockStyleContent = view.requireViewById<View>(R.id.clock_floating_sheet_style_content)
@@ -105,10 +110,10 @@
             view.requireViewById<ViewGroup>(R.id.clock_floating_sheet_font_content)
         val clockFontToolbar = view.requireViewById<ViewGroup>(R.id.clock_font_toolbar)
         clockFontToolbar.requireViewById<View>(R.id.clock_font_revert).setOnClickListener {
-            viewModel.revertFontAxes()
+            viewModel.cancelFontAxes()
         }
         clockFontToolbar.requireViewById<View>(R.id.clock_font_apply).setOnClickListener {
-            viewModel.applyFontAxes()
+            viewModel.confirmFontAxes()
         }
 
         // Clock color
@@ -137,16 +142,39 @@
         // Clock size switch
         val clockSizeSwitch = view.requireViewById<Switch>(R.id.clock_style_clock_size_switch)
 
-        view.doOnLayout {
-            if (_clockFloatingSheetHeights.value == null) {
-                _clockFloatingSheetHeights.value =
-                    ClockFloatingSheetHeightsViewModel(
-                        clockStyleContentHeight = clockStyleContent.height,
-                        clockColorContentHeight = clockColorContent.height,
-                        clockFontContentHeight = clockFontContent.height,
-                    )
+        clockStyleContent.viewTreeObserver.addOnGlobalLayoutListener(
+            object : OnGlobalLayoutListener {
+                override fun onGlobalLayout() {
+                    if (clockStyleContent.height != 0) {
+                        _clockFloatingSheetHeights.value =
+                            _clockFloatingSheetHeights.value?.copy(
+                                clockStyleContentHeight = clockStyleContent.height
+                            )
+                                ?: ClockFloatingSheetHeightsViewModel(
+                                    clockStyleContentHeight = clockStyleContent.height
+                                )
+                        clockStyleContent.viewTreeObserver.removeOnGlobalLayoutListener(this)
+                    }
+                }
             }
-        }
+        )
+
+        clockColorContent.viewTreeObserver.addOnGlobalLayoutListener(
+            object : OnGlobalLayoutListener {
+                override fun onGlobalLayout() {
+                    if (clockColorContent.height != 0) {
+                        _clockFloatingSheetHeights.value =
+                            _clockFloatingSheetHeights.value?.copy(
+                                clockColorContentHeight = clockColorContent.height
+                            )
+                                ?: ClockFloatingSheetHeightsViewModel(
+                                    clockColorContentHeight = clockColorContent.height
+                                )
+                        clockColorContent.viewTreeObserver.removeOnGlobalLayoutListener(this)
+                    }
+                }
+            }
+        )
 
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -155,27 +183,40 @@
                 launch {
                     combine(clockFloatingSheetHeights, viewModel.selectedTab, ::Pair).collect {
                         (heights, selectedTab) ->
-                        heights ?: return@collect
+                        val (clockStyleContentHeight, clockColorContentHeight) = heights
+                        clockStyleContentHeight ?: return@collect
+                        clockColorContentHeight ?: return@collect
 
-                        val targetHeight =
-                            when (selectedTab) {
-                                Tab.STYLE -> heights.clockStyleContentHeight
-                                Tab.COLOR -> heights.clockColorContentHeight
-                                Tab.FONT -> heights.clockFontContentHeight
-                            } +
-                                view.resources.getDimensionPixelSize(
-                                    R.dimen.floating_sheet_content_vertical_padding
-                                ) * 2
+                        if (selectedTab == Tab.STYLE || selectedTab == Tab.COLOR) {
+                            val targetHeight =
+                                when (selectedTab) {
+                                    Tab.STYLE -> clockStyleContentHeight
+                                    Tab.COLOR -> clockColorContentHeight
+                                    else -> 0
+                                } +
+                                    view.resources.getDimensionPixelSize(
+                                        R.dimen.floating_sheet_content_vertical_padding
+                                    ) * 2
 
-                        val animationFloatingSheet =
                             ValueAnimator.ofInt(floatingSheetContainer.height, targetHeight)
-                        animationFloatingSheet.addUpdateListener { valueAnimator ->
-                            val value = valueAnimator.animatedValue as Int
+                                .apply {
+                                    addUpdateListener { valueAnimator ->
+                                        val value = valueAnimator.animatedValue as Int
+                                        floatingSheetContainer.layoutParams =
+                                            floatingSheetContainer.layoutParams.apply {
+                                                height = value
+                                            }
+                                    }
+                                    duration = ANIMATION_DURATION
+                                }
+                                .start()
+                        } else if (selectedTab == Tab.FONT) {
                             floatingSheetContainer.layoutParams =
-                                floatingSheetContainer.layoutParams.apply { height = value }
+                                LinearLayout.LayoutParams(
+                                    LinearLayout.LayoutParams.MATCH_PARENT,
+                                    LinearLayout.LayoutParams.WRAP_CONTENT,
+                                )
                         }
-                        animationFloatingSheet.setDuration(ANIMATION_DURATION)
-                        animationFloatingSheet.start()
 
                         clockStyleContent.isVisible = selectedTab == Tab.STYLE
                         clockColorContent.isVisible = selectedTab == Tab.COLOR
@@ -192,12 +233,6 @@
                     combine(viewModel.previewingClock, viewModel.previewingFontAxisMap, ::Pair)
                         .collect { pair ->
                             val (clock, fontAxisMap) = pair
-                            if (clock == null) {
-                                boundClockId = null
-                                boundEditorViews = mapOf()
-                                clockFontContent.removeAllViews()
-                                return@collect
-                            }
 
                             if (boundClockId != clock.clockId) {
                                 boundEditorViews =
@@ -281,6 +316,7 @@
         viewModel: ClockPickerViewModel,
     ): Map<String, Pair<View, ClockFontAxis>> {
         parent.removeAllViews()
+
         val inflater = LayoutInflater.from(parent.context)
         val axisMap = mutableMapOf<String, Pair<View, ClockFontAxis>>()
         var nextSwitch: View? = null
@@ -342,27 +378,35 @@
                 )
             }
         }
+
         return axisMap
     }
 
     private fun createClockStyleOptionItemAdapter(
         lifecycleOwner: LifecycleOwner
-    ): OptionItemAdapter<ClockStyleModel> =
-        OptionItemAdapter(
-            layoutResourceId = R.layout.clock_style_option,
+    ): OptionItemAdapter2<ClockStyleModel> =
+        OptionItemAdapter2(
+            layoutResourceId = R.layout.clock_style_option2,
             lifecycleOwner = lifecycleOwner,
-            bindIcon = { view: View, style: ClockStyleModel ->
-                (view.findViewById(R.id.clock_icon) as ImageView).setImageDrawable(style.thumbnail)
-                (view.findViewById(R.id.edit_icon) as ImageView).setVisibility(
-                    // TODO(b/364673969): Route isSelected here and hide when unselected
-                    if (style.isEditable) View.VISIBLE else View.GONE
-                )
+            bindPayload = { view: View, styleModel: ClockStyleModel ->
+                view
+                    .findViewById<ImageView>(R.id.foreground)
+                    ?.setImageDrawable(styleModel.thumbnail)
+                val job =
+                    lifecycleOwner.lifecycleScope.launch {
+                        lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                            styleModel.showEditButton.collect {
+                                view.findViewById<ImageView>(R.id.edit_icon)?.isVisible = it
+                            }
+                        }
+                    }
+                return@OptionItemAdapter2 DisposableHandle { job.cancel() }
             },
         )
 
     private fun RecyclerView.initStyleList(
         context: Context,
-        adapter: OptionItemAdapter<ClockStyleModel>,
+        adapter: OptionItemAdapter2<ClockStyleModel>,
     ) {
         this.adapter = adapter
         layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
diff --git a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
index 58a1fd9..7ddcb01 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
@@ -120,6 +120,9 @@
                     )
                 val night = uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES
                 ColorOptionIconBinder2.bind(colorOptionIconView, colorIcon, night)
+                // Return null since it does not need the lifecycleOwner to launch any job for later
+                // disposal when rebind.
+                return@OptionItemAdapter2 null
             },
         )
 
diff --git a/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt
index 4c72412..838ef87 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt
@@ -156,6 +156,9 @@
                 val imageView =
                     itemView.requireViewById<ImageView>(com.android.wallpaper.R.id.foreground)
                 IconViewBinder.bind(imageView, gridIcon)
+                // Return null since it does not need the lifecycleOwner to launch any job for later
+                // disposal when rebind.
+                return@OptionItemAdapter2 null
             },
         )
 
diff --git a/src/com/android/wallpaper/customization/ui/binder/ThemePickerToolbarBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ThemePickerToolbarBinder.kt
index 18b9584..357f131 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ThemePickerToolbarBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ThemePickerToolbarBinder.kt
@@ -16,20 +16,27 @@
 
 package com.android.wallpaper.customization.ui.binder
 
+import android.animation.ValueAnimator
+import android.view.ViewTreeObserver.OnGlobalLayoutListener
 import android.widget.Button
 import android.widget.FrameLayout
 import android.widget.Toolbar
-import androidx.core.view.isVisible
+import androidx.core.view.isInvisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.wallpaper.customization.ui.viewmodel.ThemePickerCustomizationOptionsViewModel
+import com.android.wallpaper.customization.ui.viewmodel.ToolbarHeightsViewModel
 import com.android.wallpaper.picker.customization.ui.binder.DefaultToolbarBinder
 import com.android.wallpaper.picker.customization.ui.binder.ToolbarBinder
 import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationOptionsViewModel
 import javax.inject.Inject
 import javax.inject.Singleton
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.launch
 
 @Singleton
@@ -37,6 +44,9 @@
 @Inject
 constructor(private val defaultToolbarBinder: DefaultToolbarBinder) : ToolbarBinder {
 
+    private val _toolbarHeights: MutableStateFlow<ToolbarHeightsViewModel?> = MutableStateFlow(null)
+    private val toolbarHeights = _toolbarHeights.asStateFlow().filterNotNull()
+
     override fun bind(
         navButton: FrameLayout,
         toolbar: Toolbar,
@@ -60,6 +70,45 @@
             )
         }
 
+        navButton.viewTreeObserver.addOnGlobalLayoutListener(
+            object : OnGlobalLayoutListener {
+                override fun onGlobalLayout() {
+                    if (navButton.height != 0) {
+                        _toolbarHeights.value =
+                            _toolbarHeights.value?.copy(navButtonHeight = navButton.height)
+                                ?: ToolbarHeightsViewModel(navButtonHeight = navButton.height)
+                    }
+                    navButton.viewTreeObserver.removeOnGlobalLayoutListener(this)
+                }
+            }
+        )
+
+        toolbar.viewTreeObserver.addOnGlobalLayoutListener(
+            object : OnGlobalLayoutListener {
+                override fun onGlobalLayout() {
+                    if (toolbar.height != 0) {
+                        _toolbarHeights.value =
+                            _toolbarHeights.value?.copy(toolbarHeight = toolbar.height)
+                                ?: ToolbarHeightsViewModel(toolbarHeight = toolbar.height)
+                    }
+                    navButton.viewTreeObserver.removeOnGlobalLayoutListener(this)
+                }
+            }
+        )
+
+        applyButton.viewTreeObserver.addOnGlobalLayoutListener(
+            object : OnGlobalLayoutListener {
+                override fun onGlobalLayout() {
+                    if (applyButton.height != 0) {
+                        _toolbarHeights.value =
+                            _toolbarHeights.value?.copy(applyButtonHeight = applyButton.height)
+                                ?: ToolbarHeightsViewModel(applyButtonHeight = applyButton.height)
+                    }
+                    applyButton.viewTreeObserver.removeOnGlobalLayoutListener(this)
+                }
+            }
+        )
+
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
@@ -68,10 +117,60 @@
                     }
                 }
 
-                launch { viewModel.isOnApplyVisible.collect { applyButton.isVisible = it } }
+                launch { viewModel.isOnApplyVisible.collect { applyButton.isInvisible = !it } }
 
                 launch { viewModel.isOnApplyEnabled.collect { applyButton.isEnabled = it } }
+
+                launch {
+                    combine(toolbarHeights, viewModel.isToolbarCollapsed, ::Pair).collect {
+                        (toolbarHeights, isToolbarCollapsed) ->
+                        val (navButtonHeight, toolbarHeight, applyButtonHeight) = toolbarHeights
+                        navButtonHeight ?: return@collect
+                        toolbarHeight ?: return@collect
+                        applyButtonHeight ?: return@collect
+
+                        val navButtonToHeight = if (isToolbarCollapsed) 0 else navButtonHeight
+                        val toolbarToHeight = if (isToolbarCollapsed) 0 else toolbarHeight
+                        val applyButtonToHeight = if (isToolbarCollapsed) 0 else applyButtonHeight
+                        ValueAnimator.ofInt(navButton.height, navButtonToHeight)
+                            .apply {
+                                addUpdateListener { valueAnimator ->
+                                    val value = valueAnimator.animatedValue as Int
+                                    navButton.layoutParams =
+                                        navButton.layoutParams.apply { height = value }
+                                }
+                                duration = ANIMATION_DURATION
+                            }
+                            .start()
+
+                        ValueAnimator.ofInt(toolbar.height, toolbarToHeight)
+                            .apply {
+                                addUpdateListener { valueAnimator ->
+                                    val value = valueAnimator.animatedValue as Int
+                                    toolbar.layoutParams =
+                                        toolbar.layoutParams.apply { height = value }
+                                }
+                                duration = ANIMATION_DURATION
+                            }
+                            .start()
+
+                        ValueAnimator.ofInt(applyButton.height, applyButtonToHeight)
+                            .apply {
+                                addUpdateListener { valueAnimator ->
+                                    val value = valueAnimator.animatedValue as Int
+                                    applyButton.layoutParams =
+                                        applyButton.layoutParams.apply { height = value }
+                                }
+                                duration = ANIMATION_DURATION
+                            }
+                            .start()
+                    }
+                }
             }
         }
     }
+
+    companion object {
+        private const val ANIMATION_DURATION = 200L
+    }
 }
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ClockFloatingSheetHeightsViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ClockFloatingSheetHeightsViewModel.kt
index 1f86533..a39ee7d 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ClockFloatingSheetHeightsViewModel.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ClockFloatingSheetHeightsViewModel.kt
@@ -17,7 +17,6 @@
 package com.android.wallpaper.customization.ui.viewmodel
 
 data class ClockFloatingSheetHeightsViewModel(
-    val clockStyleContentHeight: Int,
-    val clockColorContentHeight: Int,
-    val clockFontContentHeight: Int,
+    val clockStyleContentHeight: Int? = null,
+    val clockColorContentHeight: Int? = null,
 )
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
index 11124f9..da3e65c 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
@@ -53,6 +53,7 @@
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
@@ -123,7 +124,7 @@
             overridingClock ?: selectedClock
         }
 
-    data class ClockStyleModel(val thumbnail: Drawable, val isEditable: Boolean)
+    data class ClockStyleModel(val thumbnail: Drawable, val showEditButton: StateFlow<Boolean>)
 
     @OptIn(ExperimentalCoroutinesApi::class)
     val clockStyleOptions: StateFlow<List<OptionItemViewModel<ClockStyleModel>>> =
@@ -131,40 +132,10 @@
             .mapLatest { allClocks ->
                 // Delay to avoid the case that the full list of clocks is not initiated.
                 delay(CLOCKS_EVENT_UPDATE_DELAY_MILLIS)
-                allClocks.map { clockModel ->
-                    val isSelectedFlow =
-                        previewingClock
-                            .map { it.clockId == clockModel.clockId }
-                            .stateIn(viewModelScope)
-                    val contentDescription =
-                        resources.getString(
-                            R.string.select_clock_action_description,
-                            clockModel.description,
-                        )
-                    OptionItemViewModel<ClockStyleModel>(
-                        key = MutableStateFlow(clockModel.clockId) as StateFlow<String>,
-                        payload =
-                            ClockStyleModel(
-                                clockModel.thumbnail,
-                                isEditable = !clockModel.fontAxes.isEmpty(),
-                            ),
-                        text = Text.Loaded(contentDescription),
-                        isTextUserVisible = false,
-                        isSelected = isSelectedFlow,
-                        onClicked =
-                            isSelectedFlow.map { isSelected ->
-                                if (isSelected) {
-                                    fun() {
-                                        _selectedTab.value = Tab.FONT
-                                    }
-                                } else {
-                                    fun() {
-                                        overridingClock.value = clockModel
-                                        overrideFontAxisMap.value = null
-                                    }
-                                }
-                            },
-                    )
+                val allClockMap = allClocks.groupBy { it.fontAxes.isNotEmpty() }
+                buildList {
+                    allClockMap[true]?.map { add(it.toOption(resources)) }
+                    allClockMap[false]?.map { add(it.toOption(resources)) }
                 }
             }
             // makes sure that the operations above this statement are executed on I/O dispatcher
@@ -173,27 +144,65 @@
             .flowOn(backgroundDispatcher.limitedParallelism(1))
             .stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
 
+    private suspend fun ClockMetadataModel.toOption(
+        resources: Resources
+    ): OptionItemViewModel<ClockStyleModel> {
+        val isSelectedFlow = previewingClock.map { it.clockId == clockId }.stateIn(viewModelScope)
+        val isEditable = fontAxes.isNotEmpty()
+        val showEditButton = isSelectedFlow.map { it && isEditable }.stateIn(viewModelScope)
+        val contentDescription =
+            resources.getString(R.string.select_clock_action_description, description)
+        return OptionItemViewModel<ClockStyleModel>(
+            key = MutableStateFlow(clockId) as StateFlow<String>,
+            payload = ClockStyleModel(thumbnail = thumbnail, showEditButton = showEditButton),
+            text = Text.Loaded(contentDescription),
+            isTextUserVisible = false,
+            isSelected = isSelectedFlow,
+            onClicked =
+                isSelectedFlow.map { isSelected ->
+                    if (isSelected && isEditable) {
+                        fun() {
+                            _selectedTab.value = Tab.FONT
+                        }
+                    } else {
+                        fun() {
+                            overridingClock.value = this
+                            overrideFontAxisMap.value = null
+                        }
+                    }
+                },
+        )
+    }
+
     // Clock Font Axis Editor
     private val overrideFontAxisMap = MutableStateFlow<Map<String, Float>?>(null)
+    private val isFontAxisMapEdited = overrideFontAxisMap.map { it != null }
+    private val selectedClockFontAxes =
+        previewingClock
+            .map { clock -> clock.fontAxes.associate { it.key to it.currentValue } }
+            .stateIn(viewModelScope, SharingStarted.Eagerly, null)
     val previewingFontAxisMap =
-        combine(overrideFontAxisMap, previewingClock) { overrideAxes, previewingClock ->
-                overrideAxes ?: previewingClock.fontAxes.associate { it.key to it.currentValue }
+        combine(overrideFontAxisMap, selectedClockFontAxes.filterNotNull()) {
+                overrideAxes,
+                selectedFontAxes ->
+                selectedFontAxes.toMutableMap().let { mutableMap ->
+                    overrideAxes?.forEach { (key, value) -> mutableMap[key] = value }
+                    mutableMap.toMap()
+                }
             }
             .stateIn(viewModelScope, SharingStarted.Eagerly, emptyMap())
 
-    private val isFontAxisMapEdited = overrideFontAxisMap.map { it != null }
-
     fun updatePreviewFontAxis(key: String, value: Float) {
-        val axisMap = previewingFontAxisMap.value.toMutableMap()
+        val axisMap = (overrideFontAxisMap.value?.toMutableMap() ?: mutableMapOf())
         axisMap[key] = value
-        overrideFontAxisMap.value = axisMap
+        overrideFontAxisMap.value = axisMap.toMap()
     }
 
-    fun applyFontAxes() {
+    fun confirmFontAxes() {
         _selectedTab.value = Tab.STYLE
     }
 
-    fun revertFontAxes() {
+    fun cancelFontAxes() {
         overrideFontAxisMap.value = null
         _selectedTab.value = Tab.STYLE
     }
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt
index b2e2039..54ae132 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt
@@ -30,6 +30,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.map
@@ -171,6 +172,14 @@
 
     val isOnApplyVisible: Flow<Boolean> = selectedOption.map { it != null }
 
+    val isToolbarCollapsed: Flow<Boolean> =
+        combine(selectedOption, clockPickerViewModel.selectedTab) { selectedOption, selectedTab ->
+                selectedOption ==
+                    ThemePickerCustomizationOptionUtil.ThemePickerLockCustomizationOption.CLOCK &&
+                    selectedTab == ClockPickerViewModel.Tab.FONT
+            }
+            .distinctUntilChanged()
+
     @ViewModelScoped
     @AssistedFactory
     interface Factory : CustomizationOptionsViewModelFactory {
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ToolbarHeightsViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ToolbarHeightsViewModel.kt
new file mode 100644
index 0000000..0d859da
--- /dev/null
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ToolbarHeightsViewModel.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wallpaper.customization.ui.viewmodel
+
+data class ToolbarHeightsViewModel(
+    val navButtonHeight: Int? = null,
+    val toolbarHeight: Int? = null,
+    val applyButtonHeight: Int? = null,
+)
diff --git a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModelTest.kt b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModelTest.kt
index ab64f89..0a8aa53 100644
--- a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModelTest.kt
+++ b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModelTest.kt
@@ -243,7 +243,7 @@
         assertThat(selectedTab()).isEqualTo(Tab.FONT)
         assertThat(previewingFontAxes()).isEqualTo(mapOf("key" to 100f))
 
-        underTest.applyFontAxes()
+        underTest.confirmFontAxes()
 
         assertThat(selectedTab()).isEqualTo(Tab.STYLE)
         assertThat(previewingFontAxes()).isEqualTo(mapOf("key" to 100f))
@@ -269,7 +269,7 @@
         assertThat(selectedTab()).isEqualTo(Tab.FONT)
         assertThat(previewingFontAxes()).isEqualTo(mapOf("key" to 100f))
 
-        underTest.revertFontAxes()
+        underTest.cancelFontAxes()
 
         assertThat(selectedTab()).isEqualTo(Tab.STYLE)
         assertThat(previewingFontAxes()).isEqualTo(mapOf("key" to 50f))