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

Commit da87a74f authored by Michael Mikhail's avatar Michael Mikhail Committed by Android (Google) Code Review
Browse files

Merge "Refactor ringer drawer constraints and add landscape constraints" into main

parents d92f5ce5 c4e92e5c
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