summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Michael Mikhail <michaelmikhil@google.com> 2024-11-19 13:56:10 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-11-19 13:56:10 +0000
commit09ba9519befd8e24c443d3d1488eb8c66831c384 (patch)
tree5f3fbb1ed498e57d8a984f3075472783b203073f
parent6a359f584fb56055054325ab897c761041c86842 (diff)
parent08c01a33df4b4b419678c1af2d34c6d49896d7d7 (diff)
Merge "Add expansion animation for ringer drawer" into main
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml4
-rw-r--r--packages/SystemUI/res/layout/volume_ringer_button.xml2
-rw-r--r--packages/SystemUI/res/layout/volume_ringer_drawer.xml55
-rw-r--r--packages/SystemUI/res/xml/volume_dialog_ringer_drawer_motion_scene.xml49
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt172
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt4
6 files changed, 201 insertions, 85 deletions
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 694357d534fb..b8544a64d9da 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -31,10 +31,10 @@
app:layout_constraintBottom_toBottomOf="@id/volume_dialog_settings"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container"
- app:layout_constraintTop_toTopOf="@id/volume_ringer_and_drawer_container" />
+ app:layout_constraintTop_toTopOf="@id/volume_ringer_drawer" />
<include
- android:id="@id/volume_ringer_and_drawer_container"
+ android:id="@id/volume_ringer_drawer"
layout="@layout/volume_ringer_drawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/volume_ringer_button.xml b/packages/SystemUI/res/layout/volume_ringer_button.xml
index dc6780aeae60..38bb783c2920 100644
--- a/packages/SystemUI/res/layout/volume_ringer_button.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_button.xml
@@ -14,6 +14,7 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
@@ -25,6 +26,7 @@
android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
android:contentDescription="@string/volume_ringer_mode"
android:gravity="center"
+ android:tint="?androidprv:attr/materialColorOnSurface"
android:src="@drawable/volume_ringer_item_bg"
android:background="@drawable/volume_ringer_item_bg"/>
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index b71c4700c0fa..d850bbe63afd 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -14,55 +14,18 @@
~ limitations under the License.
-->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/volume_ringer_and_drawer_container"
- android:layout_width="wrap_content"
+<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/volume_ringer_drawer"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
- android:layoutDirection="ltr">
+ android:layoutDirection="ltr"
+ android:orientation="vertical"
+ app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">
- <!-- Drawer view, invisible by default. -->
- <FrameLayout
- android:id="@+id/volume_drawer_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
+ <!-- add ringer buttons here -->
- <!-- View that is animated to a tapped ringer selection, so it appears selected. -->
- <FrameLayout
- android:id="@+id/volume_drawer_selection_background"
- android:layout_width="@dimen/volume_dialog_ringer_drawer_button_size"
- android:layout_height="@dimen/volume_dialog_ringer_drawer_button_size"
- android:layout_gravity="bottom|right"
- android:alpha="0.0"
- android:background="@drawable/volume_drawer_selection_bg" />
-
- <LinearLayout
- android:id="@+id/volume_drawer_options"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <!-- add ringer buttons here -->
-
- </LinearLayout>
-
- </FrameLayout>
-
- <!-- The current ringer selection. When the drawer is opened, this animates to the corresponding
- position in the drawer. When the drawer is closed, it animates back. -->
- <ImageButton
- android:id="@+id/volume_new_ringer_active_button"
- android:layout_width="@dimen/volume_dialog_ringer_drawer_button_size"
- android:layout_height="@dimen/volume_dialog_ringer_drawer_button_size"
- android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
- android:background="@drawable/volume_drawer_selection_bg"
- android:contentDescription="@string/volume_ringer_change"
- android:gravity="center"
- android:src="@drawable/ic_volume_media"
- android:tint="?androidprv:attr/materialColorOnPrimary" />
-
-</FrameLayout> \ No newline at end of file
+</androidx.constraintlayout.motion.widget.MotionLayout>
diff --git a/packages/SystemUI/res/xml/volume_dialog_ringer_drawer_motion_scene.xml b/packages/SystemUI/res/xml/volume_dialog_ringer_drawer_motion_scene.xml
new file mode 100644
index 000000000000..877637e0b0d8
--- /dev/null
+++ b/packages/SystemUI/res/xml/volume_dialog_ringer_drawer_motion_scene.xml
@@ -0,0 +1,49 @@
+<?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.
+ -->
+
+<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <Transition
+ android:id="@+id/transition"
+ app:constraintSetEnd="@+id/volume_dialog_ringer_drawer_open"
+ app:constraintSetStart="@+id/volume_dialog_ringer_drawer_close"
+ app:transitionEasing="path(0.05f, 0.7f, 0.1f, 1f)"
+ app:duration="400">
+ </Transition>
+
+ <ConstraintSet android:id="@+id/volume_dialog_ringer_drawer_close">
+ <Constraint
+ android:id="@+id/volume_ringer_drawer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+ </ConstraintSet>
+
+ <ConstraintSet android:id="@+id/volume_dialog_ringer_drawer_open">
+ <Constraint
+ android:id="@+id/volume_ringer_drawer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+ </ConstraintSet>
+
+</MotionScene> \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index c4b028d7d98b..1963ba22d444 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -18,14 +18,18 @@ package com.android.systemui.volume.dialog.ringer.ui.binder
import android.view.LayoutInflater
import android.view.View
-import android.view.ViewGroup
import android.widget.ImageButton
import androidx.annotation.LayoutRes
import androidx.compose.ui.util.fastForEachIndexed
+import androidx.constraintlayout.motion.widget.MotionLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.internal.R as internalR
+import com.android.settingslib.Utils
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
+import com.android.systemui.util.children
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonViewModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerDrawerState
@@ -33,7 +37,6 @@ import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerViewModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerViewModelState
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.VolumeDialogRingerDrawerViewModel
import javax.inject.Inject
-import kotlin.math.abs
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -44,12 +47,8 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact
fun bind(view: View) {
with(view) {
- val drawerAndRingerContainer =
- requireViewById<View>(R.id.volume_ringer_and_drawer_container)
- val drawerContainer = requireViewById<View>(R.id.volume_drawer_container)
- val selectedButtonView =
- requireViewById<ImageButton>(R.id.volume_new_ringer_active_button)
val volumeDialogBackgroundView = requireViewById<View>(R.id.volume_dialog_background)
+ val drawerContainer = requireViewById<MotionLayout>(R.id.volume_ringer_drawer)
repeatWhenAttached {
viewModel(
traceName = "VolumeDialogRingerViewBinder",
@@ -62,29 +61,26 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact
is RingerViewModelState.Available -> {
val uiModel = ringerState.uiModel
- bindSelectedButton(viewModel, uiModel, selectedButtonView)
- bindDrawerButtons(viewModel, uiModel.availableButtons)
+ bindDrawerButtons(viewModel, uiModel)
- // Set up views background and visibility
- drawerAndRingerContainer.visibility = View.VISIBLE
+ // Set up view background and visibility
+ drawerContainer.visibility = View.VISIBLE
when (uiModel.drawerState) {
is RingerDrawerState.Initial -> {
- drawerContainer.visibility = View.GONE
- selectedButtonView.visibility = View.VISIBLE
+ drawerContainer.closeDrawer(uiModel.currentButtonIndex)
volumeDialogBackgroundView.setBackgroundResource(
R.drawable.volume_dialog_background
)
}
is RingerDrawerState.Closed -> {
- drawerContainer.visibility = View.GONE
- selectedButtonView.visibility = View.VISIBLE
+ drawerContainer.closeDrawer(uiModel.currentButtonIndex)
volumeDialogBackgroundView.setBackgroundResource(
R.drawable.volume_dialog_background
)
}
is RingerDrawerState.Open -> {
- drawerContainer.visibility = View.VISIBLE
- selectedButtonView.visibility = View.GONE
+ // Open drawer
+ drawerContainer.transitionToEnd()
if (
uiModel.currentButtonIndex !=
uiModel.availableButtons.size - 1
@@ -97,7 +93,7 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact
}
}
is RingerViewModelState.Unavailable -> {
- drawerAndRingerContainer.visibility = View.GONE
+ drawerContainer.visibility = View.GONE
volumeDialogBackgroundView.setBackgroundResource(
R.drawable.volume_dialog_background
)
@@ -112,15 +108,21 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact
private fun View.bindDrawerButtons(
viewModel: VolumeDialogRingerDrawerViewModel,
- availableButtons: List<RingerButtonViewModel?>,
+ uiModel: RingerViewModel,
) {
- val drawerOptions = requireViewById<ViewGroup>(R.id.volume_drawer_options)
- val count = availableButtons.size
- drawerOptions.ensureChildCount(R.layout.volume_ringer_button, count)
+ val drawerContainer = requireViewById<MotionLayout>(R.id.volume_ringer_drawer)
+ val count = uiModel.availableButtons.size
+ drawerContainer.ensureChildCount(R.layout.volume_ringer_button, count)
- availableButtons.fastForEachIndexed { index, ringerButton ->
+ uiModel.availableButtons.fastForEachIndexed { index, ringerButton ->
ringerButton?.let {
- drawerOptions.getChildAt(count - index - 1).bindDrawerButton(it, viewModel)
+ val view = drawerContainer.getChildAt(count - index - 1)
+ // TODO (b/369995871): object animator for button switch ( active <-> inactive )
+ if (index == uiModel.currentButtonIndex) {
+ view.bindDrawerButton(uiModel.selectedButton, viewModel, isSelected = true)
+ } else {
+ view.bindDrawerButton(it, viewModel)
+ }
}
}
}
@@ -128,15 +130,29 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact
private fun View.bindDrawerButton(
buttonViewModel: RingerButtonViewModel,
viewModel: VolumeDialogRingerDrawerViewModel,
+ isSelected: Boolean = false,
) {
with(requireViewById<ImageButton>(R.id.volume_drawer_button)) {
setImageResource(buttonViewModel.imageResId)
contentDescription = context.getString(buttonViewModel.contentDescriptionResId)
- setOnClickListener { viewModel.onRingerButtonClicked(buttonViewModel.ringerMode) }
+ if (isSelected) {
+ setBackgroundResource(R.drawable.volume_drawer_selection_bg)
+ setColorFilter(
+ Utils.getColorAttrDefaultColor(context, internalR.attr.materialColorOnPrimary)
+ )
+ } else {
+ setBackgroundResource(R.drawable.volume_ringer_item_bg)
+ setColorFilter(
+ Utils.getColorAttrDefaultColor(context, internalR.attr.materialColorOnSurface)
+ )
+ }
+ setOnClickListener {
+ viewModel.onRingerButtonClicked(buttonViewModel.ringerMode, isSelected)
+ }
}
}
- private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) {
+ private fun MotionLayout.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) {
val childCountDelta = childCount - count
when {
childCountDelta > 0 -> {
@@ -144,21 +160,107 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact
}
childCountDelta < 0 -> {
val inflater = LayoutInflater.from(context)
- repeat(abs(childCountDelta)) { inflater.inflate(viewLayoutId, this, true) }
+ repeat(-childCountDelta) {
+ inflater.inflate(viewLayoutId, this, true)
+ getChildAt(childCount - 1).id = View.generateViewId()
+ }
+ cloneConstraintSet(R.id.volume_dialog_ringer_drawer_open)
+ .adjustOpenConstraintsForDrawer(this)
}
}
}
- private fun bindSelectedButton(
- viewModel: VolumeDialogRingerDrawerViewModel,
- uiModel: RingerViewModel,
- selectedButtonView: ImageButton,
+ private fun MotionLayout.closeDrawer(selectedIndex: Int) {
+ cloneConstraintSet(R.id.volume_dialog_ringer_drawer_close)
+ .adjustClosedConstraintsForDrawer(selectedIndex, this)
+ transitionToStart()
+ }
+
+ private fun ConstraintSet.adjustOpenConstraintsForDrawer(motionLayout: MotionLayout) {
+ motionLayout.children.forEachIndexed { index, button ->
+ setButtonPositionConstraints(motionLayout, index, button)
+ setAlpha(button.id, 1.0F)
+ constrainWidth(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ constrainHeight(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ if (index != motionLayout.childCount - 1) {
+ setMargin(
+ button.id,
+ ConstraintSet.BOTTOM,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_components_spacing
+ ),
+ )
+ }
+ }
+ motionLayout.updateState(R.id.volume_dialog_ringer_drawer_open, this)
+ }
+
+ private fun ConstraintSet.adjustClosedConstraintsForDrawer(
+ selectedIndex: Int,
+ motionLayout: MotionLayout,
) {
- with(uiModel) {
- selectedButtonView.setImageResource(selectedButton.imageResId)
- selectedButtonView.setOnClickListener {
- viewModel.onRingerButtonClicked(selectedButton.ringerMode)
+ motionLayout.children.forEachIndexed { index, button ->
+ setButtonPositionConstraints(motionLayout, index, button)
+ constrainWidth(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
+ if (selectedIndex != motionLayout.childCount - index - 1) {
+ setAlpha(button.id, 0.0F)
+ constrainHeight(button.id, 0)
+ setMargin(button.id, ConstraintSet.BOTTOM, 0)
+ } else {
+ setAlpha(button.id, 1.0F)
+ constrainHeight(
+ button.id,
+ motionLayout.context.resources.getDimensionPixelSize(
+ R.dimen.volume_dialog_ringer_drawer_button_size
+ ),
+ )
}
}
+ motionLayout.updateState(R.id.volume_dialog_ringer_drawer_close, this)
+ }
+
+ private fun ConstraintSet.setButtonPositionConstraints(
+ motionLayout: MotionLayout,
+ index: Int,
+ button: View,
+ ) {
+ if (motionLayout.getChildAt(index - 1) == null) {
+ connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP)
+ } else {
+ connect(
+ button.id,
+ ConstraintSet.TOP,
+ motionLayout.getChildAt(index - 1).id,
+ ConstraintSet.BOTTOM,
+ )
+ }
+
+ if (motionLayout.getChildAt(index + 1) == null) {
+ connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
+ } else {
+ connect(
+ button.id,
+ ConstraintSet.BOTTOM,
+ motionLayout.getChildAt(index + 1).id,
+ ConstraintSet.TOP,
+ )
+ }
+ connect(button.id, ConstraintSet.START, motionLayout.id, ConstraintSet.START)
+ connect(button.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
index e040638324ac..624dcc71e2a9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
@@ -84,8 +84,8 @@ constructor(
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build()
- fun onRingerButtonClicked(ringerMode: RingerMode) {
- if (drawerState.value is RingerDrawerState.Open) {
+ fun onRingerButtonClicked(ringerMode: RingerMode, isSelectedButton: Boolean = false) {
+ if (drawerState.value is RingerDrawerState.Open && !isSelectedButton) {
Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value)
provideTouchFeedback(ringerMode)
maybeShowToast(ringerMode)