New Floating Toolbar (2/2)

Use recycler view instead to implement the floating bar

Test: Manually tested. See Bug.
Bug: 350718409
Flag: com.android.wallpaper.new_picker_ui_flag
Change-Id: Ic0c8c555ef566345a14e64af51c70a48ad011422
diff --git a/res/layout/floating_sheet_clock.xml b/res/layout/floating_sheet_clock.xml
index bc77300..9c9a0ac 100644
--- a/res/layout/floating_sheet_clock.xml
+++ b/res/layout/floating_sheet_clock.xml
@@ -176,8 +176,8 @@
         </LinearLayout>
     </FrameLayout>
 
-    <com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar
-        android:id="@+id/floating_bar_tabs"
+    <com.android.wallpaper.picker.customization.ui.view.FloatingToolbar
+        android:id="@+id/floating_toolbar"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
diff --git a/res/layout/floating_sheet_colors.xml b/res/layout/floating_sheet_colors.xml
index d763054..a22b264 100644
--- a/res/layout/floating_sheet_colors.xml
+++ b/res/layout/floating_sheet_colors.xml
@@ -74,8 +74,8 @@
         </LinearLayout>
     </LinearLayout>
 
-    <com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar
-        android:id="@+id/floating_bar_tabs"
+    <com.android.wallpaper.picker.customization.ui.view.FloatingToolbar
+        android:id="@+id/floating_toolbar"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
diff --git a/res/layout/floating_sheet_shortcut.xml b/res/layout/floating_sheet_shortcut.xml
index e752ba7..fb24ef4 100644
--- a/res/layout/floating_sheet_shortcut.xml
+++ b/res/layout/floating_sheet_shortcut.xml
@@ -30,14 +30,15 @@
 
         <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/quick_affordance_horizontal_list"
-            android:layout_width="match_parent"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
             android:clipChildren="false"
             android:clipToPadding="false"/>
     </FrameLayout>
 
-    <com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar
-        android:id="@+id/floating_bar_tabs"
+    <com.android.wallpaper.picker.customization.ui.view.FloatingToolbar
+        android:id="@+id/floating_toolbar"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
diff --git a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
index 7de25e7..616640c 100644
--- a/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
+++ b/src/com/android/customization/picker/clock/ui/binder/ClockSettingsBinder.kt
@@ -45,9 +45,9 @@
 import com.android.customization.picker.clock.ui.view.ClockViewFactory
 import com.android.customization.picker.clock.ui.viewmodel.ClockSettingsViewModel
 import com.android.customization.picker.color.ui.binder.ColorOptionIconBinder
-import com.android.customization.picker.common.ui.view.ItemSpacing
 import com.android.themepicker.R
 import com.android.wallpaper.config.BaseFlags
+import com.android.wallpaper.picker.common.ui.view.ItemSpacing
 import com.android.wallpaper.picker.option.ui.binder.OptionItemBinder
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.mapNotNull
@@ -82,6 +82,7 @@
                 }
 
                 override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit
+
                 override fun onStopTrackingTouch(seekBar: SeekBar?) {
                     seekBar?.progress?.let {
                         lifecycleOwner.lifecycleScope.launch { viewModel.onSliderProgressStop(it) }
diff --git a/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt b/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
index 05e42c9..82ce77b 100644
--- a/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
+++ b/src/com/android/customization/picker/color/ui/binder/ColorPickerBinder.kt
@@ -32,8 +32,8 @@
 import com.android.customization.picker.color.ui.view.ColorOptionIconView
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
 import com.android.customization.picker.color.ui.viewmodel.ColorPickerViewModel
-import com.android.customization.picker.common.ui.view.ItemSpacing
 import com.android.themepicker.R
+import com.android.wallpaper.picker.common.ui.view.ItemSpacing
 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
diff --git a/src/com/android/customization/picker/common/ui/view/ItemSpacing.kt b/src/com/android/customization/picker/common/ui/view/ItemSpacing.kt
deleted file mode 100644
index ca689aa..0000000
--- a/src/com/android/customization/picker/common/ui/view/ItemSpacing.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2023 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.customization.picker.common.ui.view
-
-import android.graphics.Rect
-import androidx.core.view.ViewCompat
-import androidx.recyclerview.widget.RecyclerView
-
-/** Item spacing used by the RecyclerView. */
-class ItemSpacing(
-    private val itemSpacingDp: Int,
-) : RecyclerView.ItemDecoration() {
-    override fun getItemOffsets(outRect: Rect, itemPosition: Int, parent: RecyclerView) {
-        val addSpacingToStart = itemPosition > 0
-        val addSpacingToEnd = itemPosition < (parent.adapter?.itemCount ?: 0) - 1
-        val isRtl = parent.layoutManager?.layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL
-        val density = parent.context.resources.displayMetrics.density
-        val halfItemSpacingPx = itemSpacingDp.toPx(density) / 2
-        if (!isRtl) {
-            outRect.left = if (addSpacingToStart) halfItemSpacingPx else 0
-            outRect.right = if (addSpacingToEnd) halfItemSpacingPx else 0
-        } else {
-            outRect.left = if (addSpacingToEnd) halfItemSpacingPx else 0
-            outRect.right = if (addSpacingToStart) halfItemSpacingPx else 0
-        }
-    }
-
-    private fun Int.toPx(density: Float): Int {
-        return (this * density).toInt()
-    }
-
-    companion object {
-        const val TAB_ITEM_SPACING_DP = 12
-        const val ITEM_SPACING_DP = 8
-    }
-}
diff --git a/src/com/android/customization/picker/grid/ui/binder/GridScreenBinder.kt b/src/com/android/customization/picker/grid/ui/binder/GridScreenBinder.kt
index 9948dee..36d16cd 100644
--- a/src/com/android/customization/picker/grid/ui/binder/GridScreenBinder.kt
+++ b/src/com/android/customization/picker/grid/ui/binder/GridScreenBinder.kt
@@ -26,10 +26,10 @@
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
-import com.android.customization.picker.common.ui.view.ItemSpacing
 import com.android.customization.picker.grid.ui.viewmodel.GridIconViewModel
 import com.android.customization.picker.grid.ui.viewmodel.GridScreenViewModel
 import com.android.themepicker.R
+import com.android.wallpaper.picker.common.ui.view.ItemSpacing
 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
 import com.android.wallpaper.picker.option.ui.binder.OptionItemBinder
 import kotlinx.coroutines.CoroutineDispatcher
diff --git a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
index 3b583f3..9f3458c 100644
--- a/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
+++ b/src/com/android/customization/picker/quickaffordance/ui/binder/KeyguardQuickAffordancePickerBinder.kt
@@ -31,7 +31,6 @@
 import androidx.lifecycle.repeatOnLifecycle
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
-import com.android.customization.picker.common.ui.view.ItemSpacing
 import com.android.customization.picker.quickaffordance.ui.adapter.SlotTabAdapter
 import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordancePickerViewModel
 import com.android.themepicker.R
@@ -39,6 +38,7 @@
 import com.android.wallpaper.picker.common.dialog.ui.viewmodel.DialogViewModel
 import com.android.wallpaper.picker.common.icon.ui.viewbinder.IconViewBinder
 import com.android.wallpaper.picker.common.icon.ui.viewmodel.Icon
+import com.android.wallpaper.picker.common.ui.view.ItemSpacing
 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.collectIndexed
diff --git a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
index 91deed8..6f40cdf 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
@@ -44,10 +44,7 @@
 import com.android.wallpaper.customization.ui.viewmodel.ClockPickerViewModel.Tab.COLOR
 import com.android.wallpaper.customization.ui.viewmodel.ClockPickerViewModel.Tab.SIZE
 import com.android.wallpaper.customization.ui.viewmodel.ClockPickerViewModel.Tab.STYLE
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar.Tab.PRIMARY
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar.Tab.SECONDARY
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar.Tab.TERTIARY
+import com.android.wallpaper.picker.customization.ui.view.FloatingToolbar
 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -72,25 +69,7 @@
     ) {
         val appContext = view.context.applicationContext
 
-        val tabs =
-            view.requireViewById<FloatingTabToolbar>(R.id.floating_bar_tabs).apply {
-                showTertiaryTab(true)
-                setTabText(PRIMARY, appContext.getString(R.string.clock_style))
-                primaryIcon.setImageDrawable(
-                    getDrawable(appContext, R.drawable.ic_style_filled_24px)
-                )
-                setOnTabClick(PRIMARY) { viewModel.setTab(STYLE) }
-                setTabText(SECONDARY, appContext.getString(R.string.clock_color))
-                secondaryIcon.setImageDrawable(
-                    getDrawable(appContext, R.drawable.ic_palette_filled_24px)
-                )
-                setOnTabClick(SECONDARY) { viewModel.setTab(COLOR) }
-                setTabText(TERTIARY, appContext.getString(R.string.clock_size))
-                tertiaryIcon.setImageDrawable(
-                    getDrawable(appContext, R.drawable.ic_open_in_full_24px)
-                )
-                setOnTabClick(TERTIARY) { viewModel.setTab(SIZE) }
-            }
+        val tabs = view.requireViewById<FloatingToolbar>(R.id.floating_toolbar)
 
         val floatingSheetContainer =
             view.requireViewById<ViewGroup>(R.id.clock_floating_sheet_content_container)
@@ -149,6 +128,8 @@
 
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch { viewModel.tabs.collect { tabs.setItems(it) } }
+
                 launch {
                     combine(clockFloatingSheetHeights, viewModel.selectedTab) { heights, selectedTab
                             ->
@@ -156,11 +137,6 @@
                         }
                         .collect { (heights, selectedTab) ->
                             heights ?: return@collect
-                            when (selectedTab) {
-                                STYLE -> tabs.setTabSelected(PRIMARY)
-                                COLOR -> tabs.setTabSelected(SECONDARY)
-                                SIZE -> tabs.setTabSelected(TERTIARY)
-                            }
                             val targetHeight =
                                 when (selectedTab) {
                                     STYLE -> heights.clockStyleContentHeight
diff --git a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
index 6c6a587..6299a25 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ColorsFloatingSheetBinder.kt
@@ -21,7 +21,6 @@
 import android.content.res.Configuration.UI_MODE_NIGHT_YES
 import android.view.View
 import android.widget.TextView
-import androidx.core.content.res.ResourcesCompat
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.lifecycleScope
@@ -29,17 +28,13 @@
 import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
-import com.android.customization.picker.color.shared.model.ColorType
 import com.android.customization.picker.color.ui.binder.ColorOptionIconBinder
 import com.android.customization.picker.color.ui.view.ColorOptionIconView
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
-import com.android.customization.picker.color.ui.viewmodel.ColorTypeTabViewModel
 import com.android.customization.picker.common.ui.view.DoubleRowListItemSpacing
 import com.android.themepicker.R
 import com.android.wallpaper.customization.ui.viewmodel.ColorPickerViewModel2
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar.Tab.PRIMARY
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar.Tab.SECONDARY
+import com.android.wallpaper.picker.customization.ui.view.FloatingToolbar
 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
 import kotlinx.coroutines.launch
 
@@ -59,27 +54,11 @@
                 it.initColorsList(view.context.applicationContext, colorsAdapter)
             }
 
-        val tabs = view.requireViewById<FloatingTabToolbar>(R.id.floating_bar_tabs)
+        val tabs = view.requireViewById<FloatingToolbar>(R.id.floating_toolbar)
 
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
-                launch {
-                    viewModel.colorTypeTabs.collect { colorTypes ->
-                        colorTypes.forEach { (colorType, tabViewModel) ->
-                            bindTab(tabs, colorType, tabViewModel)
-                        }
-                        colorTypes
-                            .filterValues { it.isSelected }
-                            .keys
-                            .firstOrNull()
-                            ?.let {
-                                when (it) {
-                                    ColorType.WALLPAPER_COLOR -> tabs.setTabSelected(PRIMARY)
-                                    ColorType.PRESET_COLOR -> tabs.setTabSelected(SECONDARY)
-                                }
-                            }
-                    }
-                }
+                launch { viewModel.colorTypeTabs.collect { tabs.setItems(it) } }
 
                 launch { viewModel.colorTypeTabSubheader.collect { subhead.text = it } }
 
@@ -139,32 +118,4 @@
             )
         }
     }
-
-    private fun bindTab(
-        tabs: FloatingTabToolbar,
-        colorType: ColorType,
-        viewModel: ColorTypeTabViewModel
-    ) {
-        val tab =
-            when (colorType) {
-                ColorType.WALLPAPER_COLOR -> PRIMARY
-                ColorType.PRESET_COLOR -> SECONDARY
-            }
-        val iconDrawable =
-            ResourcesCompat.getDrawable(
-                tabs.resources,
-                when (colorType) {
-                    ColorType.WALLPAPER_COLOR ->
-                        com.android.wallpaper.R.drawable.ic_baseline_wallpaper_24
-                    ColorType.PRESET_COLOR -> R.drawable.ic_colors
-                },
-                null,
-            )
-        when (colorType) {
-            ColorType.WALLPAPER_COLOR -> tabs.primaryIcon.setImageDrawable(iconDrawable)
-            ColorType.PRESET_COLOR -> tabs.secondaryIcon.setImageDrawable(iconDrawable)
-        }
-        tabs.setTabText(tab, viewModel.name)
-        tabs.setOnTabClick(tab, viewModel.onClick)
-    }
 }
diff --git a/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt
index e1c1d0b..9f13888 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ShortcutFloatingSheetBinder.kt
@@ -33,18 +33,13 @@
 import com.android.wallpaper.picker.common.dialog.ui.viewmodel.DialogViewModel
 import com.android.wallpaper.picker.common.icon.ui.viewbinder.IconViewBinder
 import com.android.wallpaper.picker.common.icon.ui.viewmodel.Icon
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar.Tab
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar.Tab.PRIMARY
-import com.android.wallpaper.picker.customization.ui.view.FloatingTabToolbar.Tab.SECONDARY
+import com.android.wallpaper.picker.customization.ui.view.FloatingToolbar
 import com.android.wallpaper.picker.option.ui.adapter.OptionItemAdapter
-import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.collectIndexed
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -61,34 +56,13 @@
                 it.initQuickAffordanceList(view.context.applicationContext, quickAffordanceAdapter)
             }
 
-        val tabs = view.requireViewById<FloatingTabToolbar>(R.id.floating_bar_tabs)
+        val tabs = view.requireViewById<FloatingToolbar>(R.id.floating_toolbar)
 
         var dialog: Dialog? = null
 
         lifecycleOwner.lifecycleScope.launch {
             lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
-                launch {
-                    viewModel.slots
-                        .map { slotById -> slotById.values }
-                        .collect { slots ->
-                            val list = slots.toList()
-                            list.mapIndexed { index, slot ->
-                                val tab = if (index == 0) PRIMARY else SECONDARY
-                                tabs.setSelectedAffordances(tab, slot.selectedQuickAffordances)
-                                tabs.setTabText(tab, slot.name)
-                                tabs.setOnTabClick(tab, slot.onClicked)
-                            }
-                            list
-                                .indexOfFirst { it.isSelected }
-                                .let {
-                                    if (it == 0) {
-                                        tabs.setTabSelected(PRIMARY)
-                                    } else if (it == 1) {
-                                        tabs.setTabSelected(SECONDARY)
-                                    }
-                                }
-                        }
-                }
+                launch { viewModel.tabs.collect { tabs.setItems(it) } }
 
                 launch {
                     viewModel.quickAffordances.collect { affordances ->
@@ -199,21 +173,4 @@
             )
         }
     }
-
-    private fun FloatingTabToolbar.setSelectedAffordances(
-        tab: Tab,
-        selectedQuickAffordances: List<OptionItemViewModel<Icon>>,
-    ) {
-        val icon =
-            selectedQuickAffordances.firstOrNull()?.payload
-                ?: Icon.Resource(res = R.drawable.link_off, contentDescription = null)
-        IconViewBinder.bind(
-            if (tab == PRIMARY) {
-                this.primaryIcon
-            } else {
-                this.secondaryIcon
-            },
-            icon,
-        )
-    }
 }
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
index fc2b297..034465e 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ClockPickerViewModel.kt
@@ -32,7 +32,9 @@
 import com.android.customization.picker.color.shared.model.ColorType
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
 import com.android.themepicker.R
+import com.android.wallpaper.picker.common.icon.ui.viewmodel.Icon
 import com.android.wallpaper.picker.common.text.ui.viewmodel.Text
+import com.android.wallpaper.picker.customization.ui.viewmodel.FloatingToolbarTabViewModel
 import com.android.wallpaper.picker.di.modules.BackgroundDispatcher
 import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel
 import dagger.assisted.Assisted
@@ -82,10 +84,41 @@
 
     private val _selectedTab = MutableStateFlow(Tab.STYLE)
     val selectedTab: StateFlow<Tab> = _selectedTab.asStateFlow()
-
-    fun setTab(tab: Tab) {
-        _selectedTab.value = tab
-    }
+    val tabs: Flow<List<FloatingToolbarTabViewModel>> =
+        _selectedTab.asStateFlow().map {
+            listOf(
+                FloatingToolbarTabViewModel(
+                    Icon.Resource(
+                        res = R.drawable.ic_style_filled_24px,
+                        contentDescription = Text.Resource(R.string.clock_style),
+                    ),
+                    context.getString(R.string.clock_style),
+                    it == Tab.STYLE
+                ) {
+                    _selectedTab.value = Tab.STYLE
+                },
+                FloatingToolbarTabViewModel(
+                    Icon.Resource(
+                        res = R.drawable.ic_palette_filled_24px,
+                        contentDescription = Text.Resource(R.string.clock_color),
+                    ),
+                    context.getString(R.string.clock_color),
+                    it == Tab.COLOR
+                ) {
+                    _selectedTab.value = Tab.COLOR
+                },
+                FloatingToolbarTabViewModel(
+                    Icon.Resource(
+                        res = R.drawable.ic_open_in_full_24px,
+                        contentDescription = Text.Resource(R.string.clock_size),
+                    ),
+                    context.getString(R.string.clock_size),
+                    it == Tab.SIZE
+                ) {
+                    _selectedTab.value = Tab.SIZE
+                },
+            )
+        }
 
     @OptIn(ExperimentalCoroutinesApi::class)
     val clockStyleOptions: StateFlow<List<OptionItemViewModel<Drawable>>> =
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2.kt
index 3c77423..a039996 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2.kt
@@ -22,9 +22,10 @@
 import com.android.customization.picker.color.domain.interactor.ColorPickerInteractor
 import com.android.customization.picker.color.shared.model.ColorType
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
-import com.android.customization.picker.color.ui.viewmodel.ColorTypeTabViewModel
 import com.android.themepicker.R
+import com.android.wallpaper.picker.common.icon.ui.viewmodel.Icon
 import com.android.wallpaper.picker.common.text.ui.viewmodel.Text
+import com.android.wallpaper.picker.customization.ui.viewmodel.FloatingToolbarTabViewModel
 import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
@@ -53,35 +54,42 @@
     private val selectedColorTypeTabId = MutableStateFlow<ColorType?>(null)
 
     /** View-models for each color tab. */
-    val colorTypeTabs: Flow<Map<ColorType, ColorTypeTabViewModel>> =
+    val colorTypeTabs: Flow<List<FloatingToolbarTabViewModel>> =
         combine(
             interactor.colorOptions,
             selectedColorTypeTabId,
         ) { colorOptions, selectedColorTypeIdOrNull ->
-            colorOptions.keys
-                .mapIndexed { index, colorType ->
-                    val isSelected =
-                        (selectedColorTypeIdOrNull == null && index == 0) ||
-                            selectedColorTypeIdOrNull == colorType
-                    colorType to
-                        ColorTypeTabViewModel(
-                            name =
-                                when (colorType) {
-                                    ColorType.WALLPAPER_COLOR ->
-                                        context.resources.getString(R.string.wallpaper_color_tab)
-                                    ColorType.PRESET_COLOR ->
-                                        context.resources.getString(R.string.preset_color_tab_2)
-                                },
-                            isSelected = isSelected,
-                            onClick =
-                                if (isSelected) {
-                                    null
-                                } else {
-                                    { this.selectedColorTypeTabId.value = colorType }
-                                },
-                        )
+            colorOptions.keys.mapIndexed { index, colorType ->
+                val isSelected =
+                    (selectedColorTypeIdOrNull == null && index == 0) ||
+                        selectedColorTypeIdOrNull == colorType
+
+                val name =
+                    when (colorType) {
+                        ColorType.WALLPAPER_COLOR ->
+                            context.resources.getString(R.string.wallpaper_color_tab)
+                        ColorType.PRESET_COLOR ->
+                            context.resources.getString(R.string.preset_color_tab_2)
+                    }
+
+                FloatingToolbarTabViewModel(
+                    Icon.Resource(
+                        res =
+                            when (colorType) {
+                                ColorType.WALLPAPER_COLOR ->
+                                    com.android.wallpaper.R.drawable.ic_baseline_wallpaper_24
+                                ColorType.PRESET_COLOR -> R.drawable.ic_colors
+                            },
+                        contentDescription = Text.Loaded(name),
+                    ),
+                    name,
+                    isSelected,
+                ) {
+                    if (!isSelected) {
+                        this.selectedColorTypeTabId.value = colorType
+                    }
                 }
-                .toMap()
+            }
         }
 
     /** View-models for each color tab subheader */
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/KeyguardQuickAffordancePickerViewModel2.kt b/src/com/android/wallpaper/customization/ui/viewmodel/KeyguardQuickAffordancePickerViewModel2.kt
index a7fafe5..f717d8f 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/KeyguardQuickAffordancePickerViewModel2.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/KeyguardQuickAffordancePickerViewModel2.kt
@@ -24,7 +24,6 @@
 import com.android.customization.module.logging.ThemesUserEventLogger
 import com.android.customization.picker.quickaffordance.domain.interactor.KeyguardQuickAffordancePickerInteractor
 import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceSlotViewModel
-import com.android.customization.picker.quickaffordance.ui.viewmodel.KeyguardQuickAffordanceSummaryViewModel
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
 import com.android.themepicker.R
 import com.android.wallpaper.picker.common.button.ui.viewmodel.ButtonStyle
@@ -32,6 +31,7 @@
 import com.android.wallpaper.picker.common.dialog.ui.viewmodel.DialogViewModel
 import com.android.wallpaper.picker.common.icon.ui.viewmodel.Icon
 import com.android.wallpaper.picker.common.text.ui.viewmodel.Text
+import com.android.wallpaper.picker.customization.ui.viewmodel.FloatingToolbarTabViewModel
 import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
@@ -83,7 +83,7 @@
             )
 
     /** View-models for each slot, keyed by slot ID. */
-    val slots: Flow<Map<String, KeyguardQuickAffordanceSlotViewModel>> =
+    private val slots: Flow<Map<String, KeyguardQuickAffordanceSlotViewModel>> =
         combine(
             quickAffordanceInteractor.slots,
             quickAffordanceInteractor.affordances,
@@ -136,6 +136,18 @@
             }
         }
 
+    val tabs: Flow<List<FloatingToolbarTabViewModel>> =
+        slots.map { slotById ->
+            slotById.values.map {
+                FloatingToolbarTabViewModel(
+                    it.getIcon(),
+                    it.name,
+                    it.isSelected,
+                    it.onClicked,
+                )
+            }
+        }
+
     /**
      * The set of IDs of the currently-selected affordances. These change with user selection of new
      * or different affordances in the currently-selected slot or when slot selection changes.
@@ -246,36 +258,6 @@
                 }
         }
 
-    @SuppressLint("UseCompatLoadingForDrawables")
-    val summary: Flow<KeyguardQuickAffordanceSummaryViewModel> =
-        slots.map { slots ->
-            val icon2 =
-                (slots[KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END]
-                        ?.selectedQuickAffordances
-                        ?.firstOrNull())
-                    ?.payload
-            val icon1 =
-                (slots[KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START]
-                        ?.selectedQuickAffordances
-                        ?.firstOrNull())
-                    ?.payload
-
-            KeyguardQuickAffordanceSummaryViewModel(
-                description = toDescriptionText(applicationContext, slots),
-                icon1 =
-                    icon1
-                        ?: if (icon2 == null) {
-                            Icon.Resource(
-                                res = R.drawable.link_off,
-                                contentDescription = null,
-                            )
-                        } else {
-                            null
-                        },
-                icon2 = icon2,
-            )
-        }
-
     private val _dialog = MutableStateFlow<DialogViewModel?>(null)
     /**
      * The current dialog to show. If `null`, no dialog should be shown.
@@ -439,6 +421,12 @@
         }
     }
 
+    companion object {
+        private fun KeyguardQuickAffordanceSlotViewModel.getIcon(): Icon =
+            selectedQuickAffordances.firstOrNull()?.payload
+                ?: Icon.Resource(res = R.drawable.link_off, contentDescription = null)
+    }
+
     @ViewModelScoped
     @AssistedFactory
     interface Factory {
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 677bafe..73c9bd9 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
@@ -116,13 +116,12 @@
 
     @Test
     fun setTab() = runTest {
-        val tab = collectLastValue(underTest.selectedTab)
-        underTest.setTab(ClockPickerViewModel.Tab.STYLE)
-        assertThat(tab()).isEqualTo(ClockPickerViewModel.Tab.STYLE)
-        underTest.setTab(ClockPickerViewModel.Tab.COLOR)
-        assertThat(tab()).isEqualTo(ClockPickerViewModel.Tab.COLOR)
-        underTest.setTab(ClockPickerViewModel.Tab.SIZE)
-        assertThat(tab()).isEqualTo(ClockPickerViewModel.Tab.SIZE)
+        val tabs = collectLastValue(underTest.tabs)
+        assertThat(tabs()?.get(0)?.isSelected).isTrue()
+        tabs()?.get(1)?.onClick?.invoke()
+        assertThat(tabs()?.get(1)?.isSelected).isTrue()
+        tabs()?.get(2)?.onClick?.invoke()
+        assertThat(tabs()?.get(2)?.isSelected).isTrue()
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2Test.kt b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2Test.kt
index a43a7ce..d13d4b1 100644
--- a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2Test.kt
+++ b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ColorPickerViewModel2Test.kt
@@ -28,8 +28,8 @@
 import com.android.customization.picker.color.domain.interactor.ColorPickerSnapshotRestorer
 import com.android.customization.picker.color.shared.model.ColorType
 import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
-import com.android.customization.picker.color.ui.viewmodel.ColorTypeTabViewModel
 import com.android.systemui.monet.Style
+import com.android.wallpaper.picker.customization.ui.viewmodel.FloatingToolbarTabViewModel
 import com.android.wallpaper.picker.option.ui.viewmodel.OptionItemViewModel
 import com.android.wallpaper.testing.FakeSnapshotStore
 import com.android.wallpaper.testing.collectLastValue
@@ -116,7 +116,7 @@
             val colorOptions = collectLastValue(underTest.colorOptions)
 
             // Select "Wallpaper colors" tab
-            colorTypes()?.get(ColorType.WALLPAPER_COLOR)?.onClick?.invoke()
+            colorTypes()?.get(0)?.onClick?.invoke()
             // Select a color option
             selectColorOption(colorOptions, 0)
 
@@ -146,7 +146,7 @@
             val colorOptions = collectLastValue(underTest.colorOptions)
 
             // Select "Wallpaper colors" tab
-            colorTypes()?.get(ColorType.PRESET_COLOR)?.onClick?.invoke()
+            colorTypes()?.get(1)?.onClick?.invoke()
             // Select a color option
             selectColorOption(colorOptions, 0)
 
@@ -170,7 +170,7 @@
             )
 
             // Select "Basic colors" tab
-            colorTypes()?.get(ColorType.PRESET_COLOR)?.onClick?.invoke()
+            colorTypes()?.get(1)?.onClick?.invoke()
             assertPickerUiState(
                 colorTypes = colorTypes(),
                 colorOptions = colorOptions(),
@@ -182,7 +182,7 @@
             selectColorOption(colorOptions, 2)
 
             // Check original option is no longer selected
-            colorTypes()?.get(ColorType.WALLPAPER_COLOR)?.onClick?.invoke()
+            colorTypes()?.get(0)?.onClick?.invoke()
             assertPickerUiState(
                 colorTypes = colorTypes(),
                 colorOptions = colorOptions(),
@@ -191,7 +191,7 @@
             )
 
             // Check new option is selected
-            colorTypes()?.get(ColorType.PRESET_COLOR)?.onClick?.invoke()
+            colorTypes()?.get(1)?.onClick?.invoke()
             assertPickerUiState(
                 colorTypes = colorTypes(),
                 colorOptions = colorOptions(),
@@ -225,7 +225,7 @@
      *   -1 stands for no color option should be selected
      */
     private fun TestScope.assertPickerUiState(
-        colorTypes: Map<ColorType, ColorTypeTabViewModel>?,
+        colorTypes: List<FloatingToolbarTabViewModel>?,
         colorOptions: List<OptionItemViewModel<ColorOptionIconViewModel>>?,
         selectedColorTypeText: String,
         selectedColorOptionIndex: Int,
@@ -295,12 +295,13 @@
      * @param isSelected Whether that color type should be selected
      */
     private fun assertColorTypeTabUiState(
-        colorTypes: Map<ColorType, ColorTypeTabViewModel>?,
+        colorTypes: List<FloatingToolbarTabViewModel>?,
         colorTypeId: ColorType,
         isSelected: Boolean,
     ) {
+        val position = if (colorTypeId == ColorType.WALLPAPER_COLOR) 0 else 1
         val viewModel =
-            colorTypes?.get(colorTypeId) ?: error("No color type with ID \"$colorTypeId\"!")
+            colorTypes?.get(position) ?: error("No color type with ID \"$colorTypeId\"!")
         assertThat(viewModel.isSelected).isEqualTo(isSelected)
     }
 }