Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c4e92e5c authored by Michael Mikhail's avatar Michael Mikhail
Browse files

Refactor ringer drawer constraints and add landscape constraints

Flag: com.android.systemui.volume_redesign
Bug: 369993851
Test: checked UI.
Change-Id: I2384cf51e23bb6619d18eb89252e4280f6c656bd
parent b353840b
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@
    android:clipToPadding="false"
    android:gravity="center"
    android:layoutDirection="ltr"
    android:orientation="vertical"
    app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">

    <!-- add ringer buttons here -->
+42 −116
Original line number Diff line number Diff line
@@ -24,16 +24,16 @@ 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 androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.FloatValueHolder
import androidx.dynamicanimation.animation.SpringAnimation
import androidx.dynamicanimation.animation.SpringForce
import com.android.internal.R as internalR
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.util.VolumeDialogRingerDrawerTransitionListener
import com.android.systemui.volume.dialog.ringer.ui.util.updateCloseState
import com.android.systemui.volume.dialog.ringer.ui.util.updateOpenState
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonUiModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonViewModel
import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerDrawerState
@@ -93,6 +93,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
        }
        drawerContainer.setTransitionListener(ringerDrawerTransitionListener)
        volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate()

        viewModel.ringerViewModel
            .onEach { ringerState ->
                when (ringerState) {
@@ -110,7 +111,10 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
                                    unselectedButtonUiModel,
                                )
                                ringerDrawerTransitionListener.setProgressChangeEnabled(true)
                                drawerContainer.closeDrawer(uiModel.currentButtonIndex)
                                drawerContainer.closeDrawer(
                                    uiModel.currentButtonIndex,
                                    ringerState.orientation,
                                )
                            }

                            is RingerDrawerState.Closed -> {
@@ -125,8 +129,10 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
                                        unselectedButtonUiModel,
                                        onProgressChanged = { progress, isReverse ->
                                            // Let's make button progress when switching matches
                                            // motionLayout transition progress. When full radius,
                                            // progress is 0.0. When small radius, progress is 1.0.
                                            // motionLayout transition progress. When full
                                            // radius,
                                            // progress is 0.0. When small radius, progress is
                                            // 1.0.
                                            backgroundAnimationProgress =
                                                if (isReverse) {
                                                    1F - progress
@@ -147,7 +153,10 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
                                                true
                                            )
                                        }
                                        drawerContainer.closeDrawer(uiModel.currentButtonIndex)
                                        drawerContainer.closeDrawer(
                                            uiModel.currentButtonIndex,
                                            ringerState.orientation,
                                        )
                                    }
                                }
                            }
@@ -167,6 +176,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
                                } else {
                                    ringerDrawerTransitionListener.setProgressChangeEnabled(true)
                                }
                                updateOpenState(drawerContainer, ringerState.orientation)
                                drawerContainer.transitionToState(
                                    R.id.volume_dialog_ringer_drawer_open
                                )
@@ -223,6 +233,8 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
            // We only need to execute on roundness animation end and volume dialog background
            // progress update once because these changes should be applied once on volume dialog
            // background and ringer drawer views.
            val selectedCornerRadius = (selectedButton.background as GradientDrawable).cornerRadius
            if (selectedCornerRadius.toInt() != selectedButtonUiModel.cornerRadius) {
                selectedButton.animateTo(
                    selectedButtonUiModel,
                    if (uiModel.currentButtonIndex == count - 1) {
@@ -232,6 +244,10 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
                    },
                    roundnessAnimationEndListener,
                )
            }
            val unselectedCornerRadius =
                (unselectedButton.background as GradientDrawable).cornerRadius
            if (unselectedCornerRadius.toInt() != unselectedButtonUiModel.cornerRadius) {
                unselectedButton.animateTo(
                    unselectedButtonUiModel,
                    if (previousIndex == count - 1) {
@@ -240,6 +256,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
                        { _, _ -> }
                    },
                )
            }
        } else {
            bindButtons(viewModel, uiModel, onAnimationEnd)
        }
@@ -318,107 +335,16 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
                    inflater.inflate(viewLayoutId, this, true)
                    getChildAt(childCount - 1).id = View.generateViewId()
                }
                cloneConstraintSet(R.id.volume_dialog_ringer_drawer_open)
                    .adjustOpenConstraintsForDrawer(this)
            }
        }
    }

    private fun MotionLayout.closeDrawer(selectedIndex: Int) {
    private fun MotionLayout.closeDrawer(selectedIndex: Int, orientation: Int) {
        setTransition(R.id.close_to_open_transition)
        cloneConstraintSet(R.id.volume_dialog_ringer_drawer_close)
            .adjustClosedConstraintsForDrawer(selectedIndex, this)
        updateCloseState(this, selectedIndex, orientation)
        transitionToState(R.id.volume_dialog_ringer_drawer_close)
    }

    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,
    ) {
        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)
    }

    private suspend fun ImageButton.animateTo(
        ringerButtonUiModel: RingerButtonUiModel,
        onProgressChanged: (Float, Boolean) -> Unit = { _, _ -> },
+208 −0
Original line number Diff line number Diff line
/*
 * 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.systemui.volume.dialog.ringer.ui.util

import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.view.View
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.res.R
import com.android.systemui.util.children

fun updateOpenState(ringerDrawer: MotionLayout, orientation: Int) {
    val openSet = ringerDrawer.cloneConstraintSet(R.id.volume_dialog_ringer_drawer_open)
    openSet.adjustOpenConstraintsForDrawer(ringerDrawer, orientation)
    ringerDrawer.updateState(R.id.volume_dialog_ringer_drawer_open, openSet)
}

fun updateCloseState(ringerDrawer: MotionLayout, selectedIndex: Int, orientation: Int) {
    val closeSet = ringerDrawer.cloneConstraintSet(R.id.volume_dialog_ringer_drawer_close)
    closeSet.adjustClosedConstraintsForDrawer(ringerDrawer, selectedIndex, orientation)
    ringerDrawer.updateState(R.id.volume_dialog_ringer_drawer_close, closeSet)
}

private fun ConstraintSet.setButtonPositionPortraitConstraints(
    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)
    clear(button.id, ConstraintSet.LEFT)
    clear(button.id, ConstraintSet.RIGHT)
}

private fun ConstraintSet.setButtonPositionLandscapeConstraints(
    motionLayout: MotionLayout,
    index: Int,
    button: View,
) {
    if (motionLayout.getChildAt(index - 1) == null) {
        connect(button.id, ConstraintSet.LEFT, motionLayout.id, ConstraintSet.LEFT)
    } else {
        connect(
            button.id,
            ConstraintSet.LEFT,
            motionLayout.getChildAt(index - 1).id,
            ConstraintSet.RIGHT,
        )
    }
    if (motionLayout.getChildAt(index + 1) == null) {
        connect(button.id, ConstraintSet.RIGHT, motionLayout.id, ConstraintSet.RIGHT)
    } else {
        connect(
            button.id,
            ConstraintSet.RIGHT,
            motionLayout.getChildAt(index + 1).id,
            ConstraintSet.LEFT,
        )
    }
    connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP)
    connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
    clear(button.id, ConstraintSet.START)
    clear(button.id, ConstraintSet.END)
}

private fun ConstraintSet.adjustOpenConstraintsForDrawer(
    motionLayout: MotionLayout,
    lastOrientation: Int,
) {
    motionLayout.children.forEachIndexed { 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
            ),
        )
        when (lastOrientation) {
            ORIENTATION_LANDSCAPE -> {
                setButtonPositionLandscapeConstraints(motionLayout, index, button)
                if (index != motionLayout.childCount - 1) {
                    setMargin(
                        button.id,
                        ConstraintSet.RIGHT,
                        motionLayout.context.resources.getDimensionPixelSize(
                            R.dimen.volume_dialog_components_spacing
                        ),
                    )
                } else {
                    setMargin(button.id, ConstraintSet.RIGHT, 0)
                }
                setMargin(button.id, ConstraintSet.BOTTOM, 0)
            }
            ORIENTATION_PORTRAIT -> {
                setButtonPositionPortraitConstraints(motionLayout, index, button)
                if (index != motionLayout.childCount - 1) {
                    setMargin(
                        button.id,
                        ConstraintSet.BOTTOM,
                        motionLayout.context.resources.getDimensionPixelSize(
                            R.dimen.volume_dialog_components_spacing
                        ),
                    )
                } else {
                    setMargin(button.id, ConstraintSet.BOTTOM, 0)
                }
                setMargin(button.id, ConstraintSet.RIGHT, 0)
            }
        }
    }
}

private fun ConstraintSet.adjustClosedConstraintsForDrawer(
    motionLayout: MotionLayout,
    selectedIndex: Int,
    lastOrientation: Int,
) {
    motionLayout.children.forEachIndexed { index, button ->
        setMargin(button.id, ConstraintSet.RIGHT, 0)
        setMargin(button.id, ConstraintSet.BOTTOM, 0)
        when (lastOrientation) {
            ORIENTATION_LANDSCAPE -> {
                setButtonPositionLandscapeConstraints(motionLayout, index, button)
                if (selectedIndex != motionLayout.childCount - index - 1) {
                    setAlpha(button.id, 0.0F)
                    constrainWidth(button.id, 0)
                } else {
                    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
                    ),
                )
            }
            ORIENTATION_PORTRAIT -> {
                setButtonPositionPortraitConstraints(motionLayout, index, button)
                if (selectedIndex != motionLayout.childCount - index - 1) {
                    setAlpha(button.id, 0.0F)
                    constrainHeight(button.id, 0)
                } else {
                    setAlpha(button.id, 1.0F)
                    constrainHeight(
                        button.id,
                        motionLayout.context.resources.getDimensionPixelSize(
                            R.dimen.volume_dialog_ringer_drawer_button_size
                        ),
                    )
                }
                constrainWidth(
                    button.id,
                    motionLayout.context.resources.getDimensionPixelSize(
                        R.dimen.volume_dialog_ringer_drawer_button_size
                    ),
                )
            }
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -19,7 +19,8 @@ package com.android.systemui.volume.dialog.ringer.ui.viewmodel
/** Models ringer view model state. */
sealed class RingerViewModelState {

    data class Available(val uiModel: RingerViewModel) : RingerViewModelState()
    data class Available(val uiModel: RingerViewModel, val orientation: Int) :
        RingerViewModelState()

    data object Unavailable : RingerViewModelState()
}
+18 −3
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onConfigChanged
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
@@ -48,6 +50,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

@@ -65,19 +68,29 @@ constructor(
    private val vibrator: VibratorHelper,
    private val volumeDialogLogger: VolumeDialogLogger,
    private val visibilityInteractor: VolumeDialogVisibilityInteractor,
    configurationController: ConfigurationController,
) {

    private val drawerState = MutableStateFlow<RingerDrawerState>(RingerDrawerState.Initial)
    private val orientation: StateFlow<Int> =
        configurationController.onConfigChanged
            .map { it.orientation }
            .stateIn(
                coroutineScope,
                SharingStarted.Eagerly,
                applicationContext.resources.configuration.orientation,
            )

    val ringerViewModel: StateFlow<RingerViewModelState> =
        combine(
                soundPolicyInteractor.isZenMuted(AudioStream(STREAM_RING)),
                ringerInteractor.ringerModel,
                drawerState,
            ) { isZenMuted, ringerModel, state ->
                orientation,
            ) { isZenMuted, ringerModel, state, orientation ->
                level = ringerModel.level
                levelMax = ringerModel.levelMax
                ringerModel.toViewModel(state, isZenMuted)
                ringerModel.toViewModel(state, isZenMuted, orientation)
            }
            .flowOn(backgroundDispatcher)
            .stateIn(coroutineScope, SharingStarted.Eagerly, RingerViewModelState.Unavailable)
@@ -133,6 +146,7 @@ constructor(
    private fun VolumeDialogRingerModel.toViewModel(
        drawerState: RingerDrawerState,
        isZenMuted: Boolean,
        orientation: Int,
    ): RingerViewModelState {
        val currentIndex = availableModes.indexOf(currentRingerMode)
        if (currentIndex == -1) {
@@ -149,7 +163,8 @@ constructor(
                        currentButtonIndex = currentIndex,
                        selectedButton = it,
                        drawerState = drawerState,
                    )
                    ),
                    orientation,
                )
            } ?: RingerViewModelState.Unavailable
        }
Loading