Loading packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt +44 −32 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect Loading Loading @@ -149,7 +150,6 @@ fun FooterActions( mutableStateOf<FooterActionsForegroundServicesButtonViewModel?>(null) } var userSwitcher by remember { mutableStateOf<FooterActionsButtonViewModel?>(null) } var power by remember { mutableStateOf(viewModel.initialPower()) } var textFeedback by remember { mutableStateOf<TextFeedbackViewModel>(TextFeedbackViewModel.NoFeedback) Loading @@ -174,7 +174,6 @@ fun FooterActions( launch { viewModel.security.collect { security = it } } launch { viewModel.foregroundServices.collect { foregroundServices = it } } launch { viewModel.userSwitcher.collect { userSwitcher = it } } launch { viewModel.power.collect { power = it } } launch { viewModel.textFeedback.collect { textFeedback = it } } } } Loading Loading @@ -252,7 +251,11 @@ fun FooterActions( useModifierBasedExpandable, Modifier.sysuiResTag("settings_button_container"), ) IconButton({ power }, useModifierBasedExpandable, Modifier.sysuiResTag("pm_lite")) IconButton( { viewModel.power }, useModifierBasedExpandable, Modifier.sysuiResTag("pm_lite"), ) } } } Loading Loading @@ -325,7 +328,7 @@ private fun RowScope.ForegroundServicesButton( /** A button with an icon. */ @Composable fun IconButton( private fun IconButton( model: () -> FooterActionsButtonViewModel?, useModifierBasedExpandable: Boolean, modifier: Modifier = Modifier, Loading @@ -336,21 +339,16 @@ fun IconButton( /** A button with an icon. */ @Composable fun IconButton( private fun IconButton( model: FooterActionsButtonViewModel, useModifierBasedExpandable: Boolean, modifier: Modifier = Modifier, ) { val colors = buttonColorsForModel(model) Expandable( CircleExpandable( color = colors.background, shape = CircleShape, onClick = model.onClick, modifier = modifier.borderOnFocus( color = MaterialTheme.colorScheme.secondary, CornerSize(percent = 50), ), modifier = modifier, useModifierBasedImplementation = useModifierBasedExpandable, ) { FooterIcon(model.icon, Modifier.size(20.dp), colors.icon) Loading Loading @@ -387,16 +385,11 @@ private fun NumberButton( val interactionSource = remember { MutableInteractionSource() } val colors = numberButtonColors() Expandable( CircleExpandable( color = colors.background, shape = CircleShape, onClick = onClick, interactionSource = interactionSource, modifier = modifier.borderOnFocus( color = MaterialTheme.colorScheme.secondary, CornerSize(percent = 50), ), modifier = modifier, useModifierBasedImplementation = useModifierBasedExpandable, ) { Box(Modifier.size(40.dp)) { Loading Loading @@ -426,6 +419,34 @@ private fun NumberButton( } } @Composable private fun CircleExpandable( color: Color, modifier: Modifier = Modifier, contentColor: Color = contentColorFor(color), borderStroke: BorderStroke? = null, onClick: ((Expandable) -> Unit)? = null, interactionSource: MutableInteractionSource? = null, useModifierBasedImplementation: Boolean, content: @Composable (Expandable) -> Unit, ) { Expandable( color = color, contentColor = contentColor, borderStroke = borderStroke, shape = CircleShape, onClick = onClick, interactionSource = interactionSource, modifier = modifier.borderOnFocus( color = MaterialTheme.colorScheme.secondary, cornerSize = CornerSize(percent = 50), ), useModifierBasedImplementation = useModifierBasedImplementation, content = content, ) } /** A dot that indicates new changes. */ @Composable private fun NewChangesDot(modifier: Modifier = Modifier) { Loading @@ -448,15 +469,11 @@ private fun TextButton( modifier: Modifier = Modifier, ) { val colors = textButtonColors() Expandable( shape = CircleShape, CircleExpandable( color = colors.background, contentColor = colors.content, borderStroke = colors.border, modifier = modifier .padding(horizontal = 4.dp) .borderOnFocus(color = MaterialTheme.colorScheme.secondary, CornerSize(50)), modifier = modifier.padding(horizontal = 4.dp), onClick = onClick, useModifierBasedImplementation = useModifierBasedExpandable, ) { Loading Loading @@ -522,13 +539,8 @@ private fun numberButtonColors(): TextButtonColors { private fun buttonColorsForModel(footerAction: FooterActionsButtonViewModel): ButtonColors { return if (QsInCompose.isEnabled && notificationShadeBlur()) { when (footerAction) { is FooterActionsButtonViewModel.PowerActionViewModel -> { if (footerAction.isOnDualShade) { FooterActionsDefaults.inactiveButtonColors() } else { is FooterActionsButtonViewModel.PowerActionViewModel -> FooterActionsDefaults.activeButtonColors() } } is FooterActionsButtonViewModel.SettingsActionViewModel -> FooterActionsDefaults.inactiveButtonColors() is FooterActionsButtonViewModel.UserSwitcherViewModel -> Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt +1 −7 Original line number Diff line number Diff line Loading @@ -60,13 +60,7 @@ class FooterActionsInteractorTest : SysuiTestCase() { @Before fun setUp() { utils = FooterActionsTestUtils( context, TestableLooper.get(this), testScope.testScheduler, testScope.backgroundScope, ) utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler) } @Test Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt +18 −37 Original line number Diff line number Diff line Loading @@ -48,7 +48,6 @@ import com.android.systemui.qs.tiles.base.shared.model.FakeQSTileConfigProvider import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider import com.android.systemui.res.R import com.android.systemui.security.data.model.SecurityModel import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.connectivity.ConnectivityModule import com.android.systemui.statusbar.policy.FakeSecurityController import com.android.systemui.statusbar.policy.FakeUserInfoController Loading Loading @@ -85,13 +84,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Before fun setUp() { utils = FooterActionsTestUtils( context, TestableLooper.get(this), testScope.testScheduler, testScope.backgroundScope, ) utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler) } private fun runTest(block: suspend TestScope.() -> Unit) { Loading @@ -100,8 +93,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun settingsButton() = runTest { val underTest = utils.footerActionsViewModel(showPowerButton = false, shadeMode = ShadeMode.Single) val underTest = utils.footerActionsViewModel(showPowerButton = false) val settings = underTest.settings assertThat(settings.icon) Loading @@ -119,25 +111,22 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun powerButton() = runTest { // Without power button. val underTestWithoutPower = utils.footerActionsViewModel(showPowerButton = false, shadeMode = ShadeMode.Single) val withoutPower by collectLastValue(underTestWithoutPower.power) assertThat(withoutPower).isNull() val underTestWithoutPower = utils.footerActionsViewModel(showPowerButton = false) assertThat(underTestWithoutPower.power).isNull() // With power button. val underTestWithPower = utils.footerActionsViewModel(showPowerButton = true, shadeMode = ShadeMode.Single) val power by collectLastValue(underTestWithPower.power) assertThat(power).isNotNull() assertThat(checkNotNull(power).icon) val underTestWithPower = utils.footerActionsViewModel(showPowerButton = true) assertThat(underTestWithPower.power).isNotNull() assertThat(checkNotNull(underTestWithPower.power).icon) .isEqualTo( Icon.Resource( R.drawable.ic_qs_footer_power, ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu), ) ) assertThat(checkNotNull(power).backgroundColorFallback).isEqualTo(R.attr.shadeActive) assertThat(checkNotNull(power).iconTintFallback) assertThat(checkNotNull(underTestWithPower.power).backgroundColorFallback) .isEqualTo(R.attr.shadeActive) assertThat(checkNotNull(underTestWithPower.power).iconTintFallback) .isEqualTo(Utils.getColorAttrDefaultColor(themedContext, R.attr.onShadeActive)) } Loading @@ -159,7 +148,6 @@ class FooterActionsViewModelTest : SysuiTestCase() { val underTest = utils.footerActionsViewModel( showPowerButton = false, shadeMode = ShadeMode.Single, footerActionsInteractor = utils.footerActionsInteractor( userSwitcherRepository = Loading Loading @@ -229,13 +217,12 @@ class FooterActionsViewModelTest : SysuiTestCase() { val underTest = utils.footerActionsViewModel( shadeMode = ShadeMode.Single, footerActionsInteractor = utils.footerActionsInteractor( qsSecurityFooterUtils = qsSecurityFooterUtils, securityRepository = utils.securityRepository(securityController = securityController), ), ) ) // Collect the security model into currentSecurity. Loading Loading @@ -288,14 +275,13 @@ class FooterActionsViewModelTest : SysuiTestCase() { val underTest = utils.footerActionsViewModel( shadeMode = ShadeMode.Single, footerActionsInteractor = utils.footerActionsInteractor( qsSecurityFooterUtils = qsSecurityFooterUtils, securityRepository = utils.securityRepository(securityController), foregroundServicesRepository = utils.foregroundServicesRepository(fgsManagerController), ), ) ) // Collect the security model into currentSecurity. Loading Loading @@ -362,12 +348,11 @@ class FooterActionsViewModelTest : SysuiTestCase() { val underTest = utils.footerActionsViewModel( shadeMode = ShadeMode.Single, footerActionsInteractor = utils.footerActionsInteractor( qsSecurityFooterUtils = qsSecurityFooterUtils, broadcastDispatcher = broadcastDispatcher, ), ) ) val job = launch { underTest.observeDeviceMonitoringDialogRequests(mock()) } Loading @@ -380,7 +365,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun alpha_inSplitShade_followsExpansion() { val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Split) val underTest = utils.footerActionsViewModel() underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true) assertThat(underTest.alpha.value).isEqualTo(0f) Loading @@ -400,7 +385,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun backgroundAlpha_inSplitShade_followsExpansion_with_0_15_delay() { val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Split) val underTest = utils.footerActionsViewModel() val floatTolerance = 0.01f underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true) Loading @@ -424,7 +409,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun alpha_inSingleShade_followsExpansion_with_0_9_delay() { val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Single) val underTest = utils.footerActionsViewModel() val floatTolerance = 0.01f underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false) Loading @@ -448,7 +433,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun backgroundAlpha_inSingleShade_always1() { val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Single) val underTest = utils.footerActionsViewModel() underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false) assertThat(underTest.backgroundAlpha.value).isEqualTo(1f) Loading @@ -468,10 +453,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { val textFeedbackInteractor = utils.textFeedbackInteractor(qsTileConfigProvider = qsTileConfigProvider) val underTest = utils.footerActionsViewModel( textFeedbackInteractor = textFeedbackInteractor, shadeMode = ShadeMode.Single, ) utils.footerActionsViewModel(textFeedbackInteractor = textFeedbackInteractor) val textFeedback by collectLastValue(underTest.textFeedback) Loading Loading @@ -518,7 +500,6 @@ class FooterActionsViewModelTest : SysuiTestCase() { securityRepository = utils.securityRepository(securityController), qsSecurityFooterUtils = qsSecurityFooterUtils, ), shadeMode = ShadeMode.Single, ) val textFeedback by collectLastValue(underTest.textFeedback) Loading packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt +14 −7 Original line number Diff line number Diff line Loading @@ -18,7 +18,9 @@ package com.android.systemui.qs.footer.ui.viewmodel import android.annotation.AttrRes import android.annotation.ColorInt import android.content.Context import androidx.compose.runtime.Stable import com.android.settingslib.Utils import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon Loading @@ -38,16 +40,15 @@ sealed interface FooterActionsButtonViewModel { data class UserSwitcherViewModel( override val icon: Icon, @ColorInt override val iconTintFallback: Int?, @AttrRes override val backgroundColorFallback: Int, override val onClick: (Expandable) -> Unit, ) : FooterActionsButtonViewModel { override val id: Int = R.id.multi_user_switch @ColorInt override val iconTintFallback: Int? = null @AttrRes override val backgroundColorFallback: Int = R.attr.shadeInactive } data class SettingsActionViewModel( @ColorInt override val iconTintFallback: Int?, @AttrRes override val backgroundColorFallback: Int, private val context: Context, override val onClick: (Expandable) -> Unit, ) : FooterActionsButtonViewModel { override val id: Int = R.id.settings_button_container Loading @@ -56,12 +57,14 @@ sealed interface FooterActionsButtonViewModel { R.drawable.ic_qs_footer_settings, ContentDescription.Resource(R.string.accessibility_quick_settings_settings), ) @ColorInt override val iconTintFallback: Int = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant) @AttrRes override val backgroundColorFallback: Int = R.attr.shadeInactive } data class PowerActionViewModel( val isOnDualShade: Boolean, @ColorInt override val iconTintFallback: Int?, @AttrRes override val backgroundColorFallback: Int, private val context: Context, override val onClick: (Expandable) -> Unit, ) : FooterActionsButtonViewModel { override val id: Int = R.id.pm_lite Loading @@ -70,5 +73,9 @@ sealed interface FooterActionsButtonViewModel { R.drawable.ic_qs_footer_power, ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu), ) @ColorInt override val iconTintFallback: Int = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive) @AttrRes override val backgroundColorFallback: Int = R.attr.shadeActive } } packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt +11 −76 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleOwner import com.android.app.tracing.coroutines.launchTraced as launch import com.android.settingslib.Utils import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon Loading @@ -46,8 +45,6 @@ import com.android.systemui.qs.panels.ui.viewmodel.TextFeedbackContentViewModel. import com.android.systemui.qs.panels.ui.viewmodel.TextFeedbackViewModel import com.android.systemui.res.R import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.domain.interactor.ShadeModeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.util.icuMessageFormat import javax.inject.Inject import javax.inject.Named Loading Loading @@ -82,8 +79,9 @@ class FooterActionsViewModel( val settings: FooterActionsButtonViewModel, /** The model for the power button. */ val power: Flow<FooterActionsButtonViewModel?>, val initialPower: () -> FooterActionsButtonViewModel?, val power: FooterActionsButtonViewModel?, /** The model for the text feedback. */ val textFeedback: Flow<TextFeedbackViewModel>, /** Loading Loading @@ -127,7 +125,6 @@ class FooterActionsViewModel( @ShadeDisplayAware private val context: Context, private val falsingManager: FalsingManager, private val footerActionsInteractor: FooterActionsInteractor, private val shadeModeInteractor: ShadeModeInteractor, private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>, private val activityStarter: ActivityStarter, private val textFeedbackInteractor: TextFeedbackInteractor, Loading Loading @@ -155,7 +152,6 @@ class FooterActionsViewModel( context, footerActionsInteractor, textFeedbackInteractor, shadeModeInteractor.shadeMode, falsingManager, globalActionsDialogLite, activityStarter, Loading @@ -181,7 +177,6 @@ class FooterActionsViewModel( context, footerActionsInteractor, textFeedbackInteractor, shadeModeInteractor.shadeMode, falsingManager, globalActionsDialogLite, activityStarter, Loading @@ -195,7 +190,6 @@ fun createFooterActionsViewModel( @ShadeDisplayAware appContext: Context, footerActionsInteractor: FooterActionsInteractor, textFeedbackInteractor: TextFeedbackInteractor, shadeMode: StateFlow<ShadeMode>, falsingManager: FalsingManager, globalActionsDialogLite: GlobalActionsDialogLite, activityStarter: ActivityStarter, Loading Loading @@ -291,13 +285,13 @@ fun createFooterActionsViewModel( val userSwitcher = userSwitcherViewModel(qsThemedContext, footerActionsInteractor, ::onUserSwitcherClicked) val settings = settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked) val settings = SettingsActionViewModel(qsThemedContext, ::onSettingsButtonClicked) val power = if (showPowerButton) { powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked, shadeMode) PowerActionViewModel(qsThemedContext, ::onPowerButtonClicked) } else { flowOf(null) null } val textFeedback = Loading @@ -315,12 +309,6 @@ fun createFooterActionsViewModel( power = power, observeDeviceMonitoringDialogRequests = ::observeDeviceMonitoringDialogRequests, textFeedback = textFeedback, initialPower = if (showPowerButton) { { powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked, shadeMode.value) } } else { { null } }, ) } Loading Loading @@ -396,65 +384,12 @@ fun userSwitcherButtonViewModel( onUserSwitcherClicked: (Expandable) -> Unit, ): FooterActionsButtonViewModel { val icon = status.currentUserImage!! return UserSwitcherViewModel( icon = Icon.Loaded( icon, ContentDescription.Loaded( userSwitcherContentDescription(qsThemedContext, status.currentUserName) ), ), iconTintFallback = null, backgroundColorFallback = R.attr.shadeInactive, onClick = onUserSwitcherClicked, ) } private fun userSwitcherContentDescription( qsThemedContext: Context, currentUser: String?, ): String? { return currentUser?.let { user -> val contentDescription = status.currentUserName?.let { user -> qsThemedContext.getString(R.string.accessibility_quick_settings_user, user) } } fun settingsButtonViewModel( qsThemedContext: Context, onSettingsButtonClicked: (Expandable) -> Unit, ): FooterActionsButtonViewModel { return SettingsActionViewModel( iconTintFallback = Utils.getColorAttrDefaultColor(qsThemedContext, R.attr.onShadeInactiveVariant), backgroundColorFallback = R.attr.shadeInactive, onClick = onSettingsButtonClicked, ) } fun powerButtonViewModel( qsThemedContext: Context, onPowerButtonClicked: (Expandable) -> Unit, shadeMode: Flow<ShadeMode>, ): Flow<FooterActionsButtonViewModel?> { return shadeMode.map { mode -> powerButtonViewModel(qsThemedContext, onPowerButtonClicked, mode) } } fun powerButtonViewModel( qsThemedContext: Context, onPowerButtonClicked: (Expandable) -> Unit, shadeMode: ShadeMode, ): FooterActionsButtonViewModel { val isDualShade = shadeMode is ShadeMode.Dual return PowerActionViewModel( isOnDualShade = isDualShade, iconTintFallback = Utils.getColorAttrDefaultColor( qsThemedContext, if (isDualShade) R.attr.onShadeInactiveVariant else R.attr.onShadeActive, ), backgroundColorFallback = if (isDualShade) R.attr.shadeInactive else R.attr.shadeActive, onClick = onPowerButtonClicked, return UserSwitcherViewModel( icon = Icon.Loaded(icon, ContentDescription.Loaded(contentDescription)), onClick = onUserSwitcherClicked, ) } Loading
packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt +44 −32 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect Loading Loading @@ -149,7 +150,6 @@ fun FooterActions( mutableStateOf<FooterActionsForegroundServicesButtonViewModel?>(null) } var userSwitcher by remember { mutableStateOf<FooterActionsButtonViewModel?>(null) } var power by remember { mutableStateOf(viewModel.initialPower()) } var textFeedback by remember { mutableStateOf<TextFeedbackViewModel>(TextFeedbackViewModel.NoFeedback) Loading @@ -174,7 +174,6 @@ fun FooterActions( launch { viewModel.security.collect { security = it } } launch { viewModel.foregroundServices.collect { foregroundServices = it } } launch { viewModel.userSwitcher.collect { userSwitcher = it } } launch { viewModel.power.collect { power = it } } launch { viewModel.textFeedback.collect { textFeedback = it } } } } Loading Loading @@ -252,7 +251,11 @@ fun FooterActions( useModifierBasedExpandable, Modifier.sysuiResTag("settings_button_container"), ) IconButton({ power }, useModifierBasedExpandable, Modifier.sysuiResTag("pm_lite")) IconButton( { viewModel.power }, useModifierBasedExpandable, Modifier.sysuiResTag("pm_lite"), ) } } } Loading Loading @@ -325,7 +328,7 @@ private fun RowScope.ForegroundServicesButton( /** A button with an icon. */ @Composable fun IconButton( private fun IconButton( model: () -> FooterActionsButtonViewModel?, useModifierBasedExpandable: Boolean, modifier: Modifier = Modifier, Loading @@ -336,21 +339,16 @@ fun IconButton( /** A button with an icon. */ @Composable fun IconButton( private fun IconButton( model: FooterActionsButtonViewModel, useModifierBasedExpandable: Boolean, modifier: Modifier = Modifier, ) { val colors = buttonColorsForModel(model) Expandable( CircleExpandable( color = colors.background, shape = CircleShape, onClick = model.onClick, modifier = modifier.borderOnFocus( color = MaterialTheme.colorScheme.secondary, CornerSize(percent = 50), ), modifier = modifier, useModifierBasedImplementation = useModifierBasedExpandable, ) { FooterIcon(model.icon, Modifier.size(20.dp), colors.icon) Loading Loading @@ -387,16 +385,11 @@ private fun NumberButton( val interactionSource = remember { MutableInteractionSource() } val colors = numberButtonColors() Expandable( CircleExpandable( color = colors.background, shape = CircleShape, onClick = onClick, interactionSource = interactionSource, modifier = modifier.borderOnFocus( color = MaterialTheme.colorScheme.secondary, CornerSize(percent = 50), ), modifier = modifier, useModifierBasedImplementation = useModifierBasedExpandable, ) { Box(Modifier.size(40.dp)) { Loading Loading @@ -426,6 +419,34 @@ private fun NumberButton( } } @Composable private fun CircleExpandable( color: Color, modifier: Modifier = Modifier, contentColor: Color = contentColorFor(color), borderStroke: BorderStroke? = null, onClick: ((Expandable) -> Unit)? = null, interactionSource: MutableInteractionSource? = null, useModifierBasedImplementation: Boolean, content: @Composable (Expandable) -> Unit, ) { Expandable( color = color, contentColor = contentColor, borderStroke = borderStroke, shape = CircleShape, onClick = onClick, interactionSource = interactionSource, modifier = modifier.borderOnFocus( color = MaterialTheme.colorScheme.secondary, cornerSize = CornerSize(percent = 50), ), useModifierBasedImplementation = useModifierBasedImplementation, content = content, ) } /** A dot that indicates new changes. */ @Composable private fun NewChangesDot(modifier: Modifier = Modifier) { Loading @@ -448,15 +469,11 @@ private fun TextButton( modifier: Modifier = Modifier, ) { val colors = textButtonColors() Expandable( shape = CircleShape, CircleExpandable( color = colors.background, contentColor = colors.content, borderStroke = colors.border, modifier = modifier .padding(horizontal = 4.dp) .borderOnFocus(color = MaterialTheme.colorScheme.secondary, CornerSize(50)), modifier = modifier.padding(horizontal = 4.dp), onClick = onClick, useModifierBasedImplementation = useModifierBasedExpandable, ) { Loading Loading @@ -522,13 +539,8 @@ private fun numberButtonColors(): TextButtonColors { private fun buttonColorsForModel(footerAction: FooterActionsButtonViewModel): ButtonColors { return if (QsInCompose.isEnabled && notificationShadeBlur()) { when (footerAction) { is FooterActionsButtonViewModel.PowerActionViewModel -> { if (footerAction.isOnDualShade) { FooterActionsDefaults.inactiveButtonColors() } else { is FooterActionsButtonViewModel.PowerActionViewModel -> FooterActionsDefaults.activeButtonColors() } } is FooterActionsButtonViewModel.SettingsActionViewModel -> FooterActionsDefaults.inactiveButtonColors() is FooterActionsButtonViewModel.UserSwitcherViewModel -> Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt +1 −7 Original line number Diff line number Diff line Loading @@ -60,13 +60,7 @@ class FooterActionsInteractorTest : SysuiTestCase() { @Before fun setUp() { utils = FooterActionsTestUtils( context, TestableLooper.get(this), testScope.testScheduler, testScope.backgroundScope, ) utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler) } @Test Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt +18 −37 Original line number Diff line number Diff line Loading @@ -48,7 +48,6 @@ import com.android.systemui.qs.tiles.base.shared.model.FakeQSTileConfigProvider import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider import com.android.systemui.res.R import com.android.systemui.security.data.model.SecurityModel import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.connectivity.ConnectivityModule import com.android.systemui.statusbar.policy.FakeSecurityController import com.android.systemui.statusbar.policy.FakeUserInfoController Loading Loading @@ -85,13 +84,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Before fun setUp() { utils = FooterActionsTestUtils( context, TestableLooper.get(this), testScope.testScheduler, testScope.backgroundScope, ) utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler) } private fun runTest(block: suspend TestScope.() -> Unit) { Loading @@ -100,8 +93,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun settingsButton() = runTest { val underTest = utils.footerActionsViewModel(showPowerButton = false, shadeMode = ShadeMode.Single) val underTest = utils.footerActionsViewModel(showPowerButton = false) val settings = underTest.settings assertThat(settings.icon) Loading @@ -119,25 +111,22 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun powerButton() = runTest { // Without power button. val underTestWithoutPower = utils.footerActionsViewModel(showPowerButton = false, shadeMode = ShadeMode.Single) val withoutPower by collectLastValue(underTestWithoutPower.power) assertThat(withoutPower).isNull() val underTestWithoutPower = utils.footerActionsViewModel(showPowerButton = false) assertThat(underTestWithoutPower.power).isNull() // With power button. val underTestWithPower = utils.footerActionsViewModel(showPowerButton = true, shadeMode = ShadeMode.Single) val power by collectLastValue(underTestWithPower.power) assertThat(power).isNotNull() assertThat(checkNotNull(power).icon) val underTestWithPower = utils.footerActionsViewModel(showPowerButton = true) assertThat(underTestWithPower.power).isNotNull() assertThat(checkNotNull(underTestWithPower.power).icon) .isEqualTo( Icon.Resource( R.drawable.ic_qs_footer_power, ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu), ) ) assertThat(checkNotNull(power).backgroundColorFallback).isEqualTo(R.attr.shadeActive) assertThat(checkNotNull(power).iconTintFallback) assertThat(checkNotNull(underTestWithPower.power).backgroundColorFallback) .isEqualTo(R.attr.shadeActive) assertThat(checkNotNull(underTestWithPower.power).iconTintFallback) .isEqualTo(Utils.getColorAttrDefaultColor(themedContext, R.attr.onShadeActive)) } Loading @@ -159,7 +148,6 @@ class FooterActionsViewModelTest : SysuiTestCase() { val underTest = utils.footerActionsViewModel( showPowerButton = false, shadeMode = ShadeMode.Single, footerActionsInteractor = utils.footerActionsInteractor( userSwitcherRepository = Loading Loading @@ -229,13 +217,12 @@ class FooterActionsViewModelTest : SysuiTestCase() { val underTest = utils.footerActionsViewModel( shadeMode = ShadeMode.Single, footerActionsInteractor = utils.footerActionsInteractor( qsSecurityFooterUtils = qsSecurityFooterUtils, securityRepository = utils.securityRepository(securityController = securityController), ), ) ) // Collect the security model into currentSecurity. Loading Loading @@ -288,14 +275,13 @@ class FooterActionsViewModelTest : SysuiTestCase() { val underTest = utils.footerActionsViewModel( shadeMode = ShadeMode.Single, footerActionsInteractor = utils.footerActionsInteractor( qsSecurityFooterUtils = qsSecurityFooterUtils, securityRepository = utils.securityRepository(securityController), foregroundServicesRepository = utils.foregroundServicesRepository(fgsManagerController), ), ) ) // Collect the security model into currentSecurity. Loading Loading @@ -362,12 +348,11 @@ class FooterActionsViewModelTest : SysuiTestCase() { val underTest = utils.footerActionsViewModel( shadeMode = ShadeMode.Single, footerActionsInteractor = utils.footerActionsInteractor( qsSecurityFooterUtils = qsSecurityFooterUtils, broadcastDispatcher = broadcastDispatcher, ), ) ) val job = launch { underTest.observeDeviceMonitoringDialogRequests(mock()) } Loading @@ -380,7 +365,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun alpha_inSplitShade_followsExpansion() { val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Split) val underTest = utils.footerActionsViewModel() underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true) assertThat(underTest.alpha.value).isEqualTo(0f) Loading @@ -400,7 +385,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun backgroundAlpha_inSplitShade_followsExpansion_with_0_15_delay() { val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Split) val underTest = utils.footerActionsViewModel() val floatTolerance = 0.01f underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true) Loading @@ -424,7 +409,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun alpha_inSingleShade_followsExpansion_with_0_9_delay() { val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Single) val underTest = utils.footerActionsViewModel() val floatTolerance = 0.01f underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false) Loading @@ -448,7 +433,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { @Test fun backgroundAlpha_inSingleShade_always1() { val underTest = utils.footerActionsViewModel(shadeMode = ShadeMode.Single) val underTest = utils.footerActionsViewModel() underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false) assertThat(underTest.backgroundAlpha.value).isEqualTo(1f) Loading @@ -468,10 +453,7 @@ class FooterActionsViewModelTest : SysuiTestCase() { val textFeedbackInteractor = utils.textFeedbackInteractor(qsTileConfigProvider = qsTileConfigProvider) val underTest = utils.footerActionsViewModel( textFeedbackInteractor = textFeedbackInteractor, shadeMode = ShadeMode.Single, ) utils.footerActionsViewModel(textFeedbackInteractor = textFeedbackInteractor) val textFeedback by collectLastValue(underTest.textFeedback) Loading Loading @@ -518,7 +500,6 @@ class FooterActionsViewModelTest : SysuiTestCase() { securityRepository = utils.securityRepository(securityController), qsSecurityFooterUtils = qsSecurityFooterUtils, ), shadeMode = ShadeMode.Single, ) val textFeedback by collectLastValue(underTest.textFeedback) Loading
packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt +14 −7 Original line number Diff line number Diff line Loading @@ -18,7 +18,9 @@ package com.android.systemui.qs.footer.ui.viewmodel import android.annotation.AttrRes import android.annotation.ColorInt import android.content.Context import androidx.compose.runtime.Stable import com.android.settingslib.Utils import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon Loading @@ -38,16 +40,15 @@ sealed interface FooterActionsButtonViewModel { data class UserSwitcherViewModel( override val icon: Icon, @ColorInt override val iconTintFallback: Int?, @AttrRes override val backgroundColorFallback: Int, override val onClick: (Expandable) -> Unit, ) : FooterActionsButtonViewModel { override val id: Int = R.id.multi_user_switch @ColorInt override val iconTintFallback: Int? = null @AttrRes override val backgroundColorFallback: Int = R.attr.shadeInactive } data class SettingsActionViewModel( @ColorInt override val iconTintFallback: Int?, @AttrRes override val backgroundColorFallback: Int, private val context: Context, override val onClick: (Expandable) -> Unit, ) : FooterActionsButtonViewModel { override val id: Int = R.id.settings_button_container Loading @@ -56,12 +57,14 @@ sealed interface FooterActionsButtonViewModel { R.drawable.ic_qs_footer_settings, ContentDescription.Resource(R.string.accessibility_quick_settings_settings), ) @ColorInt override val iconTintFallback: Int = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant) @AttrRes override val backgroundColorFallback: Int = R.attr.shadeInactive } data class PowerActionViewModel( val isOnDualShade: Boolean, @ColorInt override val iconTintFallback: Int?, @AttrRes override val backgroundColorFallback: Int, private val context: Context, override val onClick: (Expandable) -> Unit, ) : FooterActionsButtonViewModel { override val id: Int = R.id.pm_lite Loading @@ -70,5 +73,9 @@ sealed interface FooterActionsButtonViewModel { R.drawable.ic_qs_footer_power, ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu), ) @ColorInt override val iconTintFallback: Int = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive) @AttrRes override val backgroundColorFallback: Int = R.attr.shadeActive } }
packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt +11 −76 Original line number Diff line number Diff line Loading @@ -24,7 +24,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleOwner import com.android.app.tracing.coroutines.launchTraced as launch import com.android.settingslib.Utils import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon Loading @@ -46,8 +45,6 @@ import com.android.systemui.qs.panels.ui.viewmodel.TextFeedbackContentViewModel. import com.android.systemui.qs.panels.ui.viewmodel.TextFeedbackViewModel import com.android.systemui.res.R import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.shade.domain.interactor.ShadeModeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.util.icuMessageFormat import javax.inject.Inject import javax.inject.Named Loading Loading @@ -82,8 +79,9 @@ class FooterActionsViewModel( val settings: FooterActionsButtonViewModel, /** The model for the power button. */ val power: Flow<FooterActionsButtonViewModel?>, val initialPower: () -> FooterActionsButtonViewModel?, val power: FooterActionsButtonViewModel?, /** The model for the text feedback. */ val textFeedback: Flow<TextFeedbackViewModel>, /** Loading Loading @@ -127,7 +125,6 @@ class FooterActionsViewModel( @ShadeDisplayAware private val context: Context, private val falsingManager: FalsingManager, private val footerActionsInteractor: FooterActionsInteractor, private val shadeModeInteractor: ShadeModeInteractor, private val globalActionsDialogLiteProvider: Provider<GlobalActionsDialogLite>, private val activityStarter: ActivityStarter, private val textFeedbackInteractor: TextFeedbackInteractor, Loading Loading @@ -155,7 +152,6 @@ class FooterActionsViewModel( context, footerActionsInteractor, textFeedbackInteractor, shadeModeInteractor.shadeMode, falsingManager, globalActionsDialogLite, activityStarter, Loading @@ -181,7 +177,6 @@ class FooterActionsViewModel( context, footerActionsInteractor, textFeedbackInteractor, shadeModeInteractor.shadeMode, falsingManager, globalActionsDialogLite, activityStarter, Loading @@ -195,7 +190,6 @@ fun createFooterActionsViewModel( @ShadeDisplayAware appContext: Context, footerActionsInteractor: FooterActionsInteractor, textFeedbackInteractor: TextFeedbackInteractor, shadeMode: StateFlow<ShadeMode>, falsingManager: FalsingManager, globalActionsDialogLite: GlobalActionsDialogLite, activityStarter: ActivityStarter, Loading Loading @@ -291,13 +285,13 @@ fun createFooterActionsViewModel( val userSwitcher = userSwitcherViewModel(qsThemedContext, footerActionsInteractor, ::onUserSwitcherClicked) val settings = settingsButtonViewModel(qsThemedContext, ::onSettingsButtonClicked) val settings = SettingsActionViewModel(qsThemedContext, ::onSettingsButtonClicked) val power = if (showPowerButton) { powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked, shadeMode) PowerActionViewModel(qsThemedContext, ::onPowerButtonClicked) } else { flowOf(null) null } val textFeedback = Loading @@ -315,12 +309,6 @@ fun createFooterActionsViewModel( power = power, observeDeviceMonitoringDialogRequests = ::observeDeviceMonitoringDialogRequests, textFeedback = textFeedback, initialPower = if (showPowerButton) { { powerButtonViewModel(qsThemedContext, ::onPowerButtonClicked, shadeMode.value) } } else { { null } }, ) } Loading Loading @@ -396,65 +384,12 @@ fun userSwitcherButtonViewModel( onUserSwitcherClicked: (Expandable) -> Unit, ): FooterActionsButtonViewModel { val icon = status.currentUserImage!! return UserSwitcherViewModel( icon = Icon.Loaded( icon, ContentDescription.Loaded( userSwitcherContentDescription(qsThemedContext, status.currentUserName) ), ), iconTintFallback = null, backgroundColorFallback = R.attr.shadeInactive, onClick = onUserSwitcherClicked, ) } private fun userSwitcherContentDescription( qsThemedContext: Context, currentUser: String?, ): String? { return currentUser?.let { user -> val contentDescription = status.currentUserName?.let { user -> qsThemedContext.getString(R.string.accessibility_quick_settings_user, user) } } fun settingsButtonViewModel( qsThemedContext: Context, onSettingsButtonClicked: (Expandable) -> Unit, ): FooterActionsButtonViewModel { return SettingsActionViewModel( iconTintFallback = Utils.getColorAttrDefaultColor(qsThemedContext, R.attr.onShadeInactiveVariant), backgroundColorFallback = R.attr.shadeInactive, onClick = onSettingsButtonClicked, ) } fun powerButtonViewModel( qsThemedContext: Context, onPowerButtonClicked: (Expandable) -> Unit, shadeMode: Flow<ShadeMode>, ): Flow<FooterActionsButtonViewModel?> { return shadeMode.map { mode -> powerButtonViewModel(qsThemedContext, onPowerButtonClicked, mode) } } fun powerButtonViewModel( qsThemedContext: Context, onPowerButtonClicked: (Expandable) -> Unit, shadeMode: ShadeMode, ): FooterActionsButtonViewModel { val isDualShade = shadeMode is ShadeMode.Dual return PowerActionViewModel( isOnDualShade = isDualShade, iconTintFallback = Utils.getColorAttrDefaultColor( qsThemedContext, if (isDualShade) R.attr.onShadeInactiveVariant else R.attr.onShadeActive, ), backgroundColorFallback = if (isDualShade) R.attr.shadeInactive else R.attr.shadeActive, onClick = onPowerButtonClicked, return UserSwitcherViewModel( icon = Icon.Loaded(icon, ContentDescription.Loaded(contentDescription)), onClick = onUserSwitcherClicked, ) }