Clock floating sheet height animation
Clock floating sheet height should change with the content inside.
Test: Manually tested. See bug.
Bug: 350718184
Flag: com.android.wallpaper.new_picker_ui_flag
Change-Id: Ibe2832ef9471197a6804576b314772f351a83e2d
diff --git a/res/layout/floating_sheet_clock.xml b/res/layout/floating_sheet_clock.xml
index ff3a83a..4f114a6 100644
--- a/res/layout/floating_sheet_clock.xml
+++ b/res/layout/floating_sheet_clock.xml
@@ -21,6 +21,7 @@
android:orientation="vertical">
<FrameLayout
+ android:id="@+id/clock_floating_sheet_content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="@dimen/floating_sheet_content_vertical_padding"
@@ -37,7 +38,7 @@
<LinearLayout
- android:id="@+id/clock_color_sheet_content"
+ android:id="@+id/clock_floating_sheet_color_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -45,30 +46,36 @@
android:clipChildren="false">
<FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/clock_color_list"
+ android:id="@+id/clock_color_list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:clipToPadding="false"
android:clipChildren="false"
- android:clipToPadding="false"/>
+ android:layout_marginBottom="12dp">
<!--
- This is an invisible placeholder put in place so that the parent keeps its height
- stable as the RecyclerView updates from 0 items to N items. Keeping it stable allows
- the layout logic to keep the size of the preview container stable as well, which
- bodes well for setting up the SurfaceView for remote rendering without changing its
- size after the content is loaded into the RecyclerView.
+ This is an invisible placeholder put in place so that the parent keeps its height
+ stable as the RecyclerView updates from 0 items to N items. Keeping it stable allows
+ the layout logic to keep the size of the preview container stable as well, which
+ bodes well for setting up the SurfaceView for remote rendering without changing its
+ size after the content is loaded into the RecyclerView.
- It's critical for any TextViews inside the included layout to have text.
- -->
+ It's critical for any TextViews inside the included layout to have text.
+ -->
<include
layout="@layout/clock_color_option_noop_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible" />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/clock_color_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false" />
+
+
</FrameLayout>
@@ -87,7 +94,7 @@
</LinearLayout>
<LinearLayout
- android:id="@+id/clock_size_container"
+ android:id="@+id/clock_floating_sheet_size_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
diff --git a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
index 4cb9bb5..335d4fe 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ClockFloatingSheetBinder.kt
@@ -16,13 +16,16 @@
package com.android.wallpaper.customization.ui.binder
+import android.animation.ValueAnimator
import android.annotation.DrawableRes
import android.content.Context
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.view.View
+import android.view.ViewGroup
import android.widget.SeekBar
import androidx.core.content.res.ResourcesCompat
+import androidx.core.view.doOnLayout
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
@@ -36,6 +39,7 @@
import com.android.customization.picker.color.ui.viewmodel.ColorOptionIconViewModel
import com.android.customization.picker.common.ui.view.DoubleRowListItemSpacing
import com.android.themepicker.R
+import com.android.wallpaper.customization.ui.viewmodel.ClockFloatingSheetHeightsViewModel
import com.android.wallpaper.customization.ui.viewmodel.ClockPickerViewModel
import com.android.wallpaper.customization.ui.viewmodel.ClockPickerViewModel.Tab.COLOR
import com.android.wallpaper.customization.ui.viewmodel.ClockPickerViewModel.Tab.SIZE
@@ -45,11 +49,21 @@
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.option.ui.adapter.OptionItemAdapter
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
object ClockFloatingSheetBinder {
private const val SLIDER_ENABLED_ALPHA = 1f
private const val SLIDER_DISABLED_ALPHA = .3f
+ private const val ANIMATION_DURATION = 200L
+
+ private val _clockFloatingSheetHeights: MutableStateFlow<ClockFloatingSheetHeightsViewModel?> =
+ MutableStateFlow(null)
+ private val clockFloatingSheetHeights: Flow<ClockFloatingSheetHeightsViewModel?> =
+ _clockFloatingSheetHeights.asStateFlow()
fun bind(
view: View,
@@ -78,11 +92,15 @@
setOnTabClick(TERTIARY) { viewModel.setTab(SIZE) }
}
+ val container = view.requireViewById<ViewGroup>(R.id.clock_floating_sheet_content_container)
+
// Clock style
val clockList = view.requireViewById<View>(R.id.clock_list)
// Cloc color
- val clockColorSheetContent = view.requireViewById<View>(R.id.clock_color_sheet_content)
+ val clockColorSheetContent =
+ view.requireViewById<View>(R.id.clock_floating_sheet_color_content)
+ val clockColorListContainer = view.requireViewById<View>(R.id.clock_color_list_container)
val clockColorAdapter =
createOptionItemAdapter(
view.resources.configuration.uiMode,
@@ -112,22 +130,67 @@
)
// Clock size
- val clockSizeContainer = view.requireViewById<View>(R.id.clock_size_container)
+ val clockSizeContainer = view.requireViewById<View>(R.id.clock_floating_sheet_size_content)
+
+ view.doOnLayout {
+ if (_clockFloatingSheetHeights.value == null) {
+ _clockFloatingSheetHeights.value =
+ ClockFloatingSheetHeightsViewModel(
+ clockStyleContentHeight = clockList.height,
+ clockColorContentHeight = clockColorSheetContent.height,
+ clockColorListHeight = clockColorListContainer.height,
+ clockSizeContentHeight = clockSizeContainer.height,
+ )
+ }
+ }
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- viewModel.selectedTab.collect {
- when (it) {
- STYLE -> tabs.setTabSelected(PRIMARY)
- COLOR -> tabs.setTabSelected(SECONDARY)
- SIZE -> tabs.setTabSelected(TERTIARY)
+ combine(clockFloatingSheetHeights, viewModel.selectedTab) { heights, selectedTab
+ ->
+ heights to selectedTab
}
+ .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
+ COLOR -> heights.clockColorContentHeight
+ SIZE -> heights.clockSizeContentHeight
+ } +
+ view.resources.getDimensionPixelSize(
+ R.dimen.floating_sheet_content_vertical_padding
+ ) * 2
- clockList.isVisible = it == STYLE
- clockColorSheetContent.isVisible = it == COLOR
- clockSizeContainer.isVisible = it == SIZE
- }
+ val animationFloatingSheet =
+ ValueAnimator.ofInt(container.height, targetHeight)
+ animationFloatingSheet.addUpdateListener { valueAnimator ->
+ val value = valueAnimator.animatedValue as Int
+ container.layoutParams =
+ container.layoutParams.apply { height = value }
+ }
+ animationFloatingSheet.setDuration(ANIMATION_DURATION)
+ animationFloatingSheet.start()
+
+ // For some reason the color list layout collapses when we animate the
+ // parent's height. This is to make sure the height stays as it is
+ // firstly
+ // inflated.
+ clockColorList.layoutParams =
+ clockColorList.layoutParams.apply {
+ height = heights.clockColorListHeight
+ }
+
+ clockList.isVisible = selectedTab == STYLE
+ clockColorSheetContent.isVisible = selectedTab == COLOR
+ clockSizeContainer.isVisible = selectedTab == SIZE
+ }
}
launch {
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ClockFloatingSheetHeightsViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ClockFloatingSheetHeightsViewModel.kt
new file mode 100644
index 0000000..37845cd
--- /dev/null
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ClockFloatingSheetHeightsViewModel.kt
@@ -0,0 +1,24 @@
+/*
+ * 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 ClockFloatingSheetHeightsViewModel(
+ val clockStyleContentHeight: Int,
+ val clockColorContentHeight: Int,
+ val clockColorListHeight: Int,
+ val clockSizeContentHeight: Int,
+)