Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt +5 −1 Original line number Diff line number Diff line Loading @@ -117,7 +117,11 @@ class InternetTileMapperTest : SysuiTestCase() { label, activationState, secondaryLabel, setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), setOf( QSTileState.UserAction.CLICK, QSTileState.UserAction.TOGGLE_CLICK, QSTileState.UserAction.LONG_CLICK ), contentDescription, null, QSTileState.SideViewIcon.Chevron, Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt +26 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandl import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx import com.android.systemui.qs.tiles.dialog.InternetDialogManager import com.android.systemui.qs.tiles.dialog.WifiStateWorker import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel import com.android.systemui.statusbar.connectivity.AccessPointController import com.android.systemui.util.mockito.mock Loading @@ -40,6 +41,8 @@ import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.kotlin.times import org.mockito.kotlin.whenever @SmallTest @EnabledOnRavenwood Loading @@ -51,17 +54,20 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() { private lateinit var underTest: InternetTileUserActionInteractor @Mock private lateinit var internetDialogManager: InternetDialogManager @Mock private lateinit var wifiStateWorker: WifiStateWorker @Mock private lateinit var controller: AccessPointController @Before fun setup() { internetDialogManager = mock<InternetDialogManager>() wifiStateWorker = mock<WifiStateWorker>() controller = mock<AccessPointController>() underTest = InternetTileUserActionInteractor( kosmos.testScope.coroutineContext, internetDialogManager, wifiStateWorker, controller, inputHandler, ) Loading Loading @@ -110,4 +116,24 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() { Truth.assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS) } } @Test fun handleSecondaryClickWhenWifiOn() = kosmos.testScope.runTest { whenever(wifiStateWorker.isWifiEnabled).thenReturn(true) underTest.handleInput(QSTileInputTestKtx.toggleClick(InternetTileModel.Active())) verify(wifiStateWorker, times(1)).isWifiEnabled = eq(false) } @Test fun handleSecondaryClickWhenWifiOff() = kosmos.testScope.runTest { whenever(wifiStateWorker.isWifiEnabled).thenReturn(false) underTest.handleInput(QSTileInputTestKtx.toggleClick(InternetTileModel.Inactive())) verify(wifiStateWorker, times(1)).isWifiEnabled = eq(true) } } packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +4 −0 Original line number Diff line number Diff line Loading @@ -169,6 +169,7 @@ public interface QSTile { public boolean isTransient = false; public String expandedAccessibilityClassName; public boolean handlesLongClick = true; public boolean handlesSecondaryClick = false; @Nullable public Drawable sideViewCustomDrawable; public String spec; Loading Loading @@ -212,6 +213,7 @@ public interface QSTile { || !Objects.equals(other.isTransient, isTransient) || !Objects.equals(other.dualTarget, dualTarget) || !Objects.equals(other.handlesLongClick, handlesLongClick) || !Objects.equals(other.handlesSecondaryClick, handlesSecondaryClick) || !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable); other.spec = spec; other.icon = icon; Loading @@ -227,6 +229,7 @@ public interface QSTile { other.dualTarget = dualTarget; other.isTransient = isTransient; other.handlesLongClick = handlesLongClick; other.handlesSecondaryClick = handlesSecondaryClick; other.sideViewCustomDrawable = sideViewCustomDrawable; return changed; } Loading @@ -252,6 +255,7 @@ public interface QSTile { sb.append(",disabledByPolicy=").append(disabledByPolicy); sb.append(",dualTarget=").append(dualTarget); sb.append(",isTransient=").append(isTransient); sb.append(",handlesSecondaryClick=").append(handlesSecondaryClick); sb.append(",state=").append(state); sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable); return sb.append(']'); Loading packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt +45 −31 Original line number Diff line number Diff line Loading @@ -109,6 +109,7 @@ import com.android.systemui.qs.panels.ui.model.GridCell import com.android.systemui.qs.panels.ui.model.SpacerGridCell import com.android.systemui.qs.panels.ui.model.TileGridCell import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileUiState import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel import com.android.systemui.qs.panels.ui.viewmodel.toUiState import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor Loading @@ -129,7 +130,7 @@ fun Tile( ) { val state by tile.state.collectAsStateWithLifecycle(tile.currentState) val uiState = remember(state) { state.toUiState() } val colors = TileDefaults.getColorForState(uiState.state) val colors = TileDefaults.getColorForState(uiState) TileContainer( colors = colors, Loading @@ -150,9 +151,13 @@ fun Tile( secondaryLabel = uiState.secondaryLabel, icon = icon, colors = colors, clickEnabled = true, onClick = tile::onSecondaryClick, onLongClick = tile::onLongClick, toggleClickSupported = state.handlesSecondaryClick, onClick = { if (state.handlesSecondaryClick) { tile.onSecondaryClick() } }, onLongClick = { tile.onLongClick(it) }, ) } } Loading @@ -168,7 +173,7 @@ private fun TileContainer( onClick: (Expandable) -> Unit = {}, onLongClick: (Expandable) -> Unit = {}, modifier: Modifier = Modifier, content: @Composable BoxScope.() -> Unit, content: @Composable BoxScope.(Expandable) -> Unit, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, Loading Loading @@ -200,7 +205,7 @@ private fun TileContainer( } .tilePadding(), ) { content() content(it) } } Loading @@ -222,36 +227,27 @@ private fun LargeTileContent( secondaryLabel: String?, icon: Icon, colors: TileColors, clickEnabled: Boolean = false, onClick: (Expandable) -> Unit = {}, onLongClick: (Expandable) -> Unit = {}, toggleClickSupported: Boolean = false, onClick: () -> Unit = {}, onLongClick: () -> Unit = {}, ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = tileHorizontalArrangement() ) { Expandable( color = colors.iconBackground, shape = TileDefaults.TileShape, modifier = Modifier.fillMaxHeight().aspectRatio(1f) ) { // Icon Box( modifier = Modifier.fillMaxSize().clip(TileDefaults.TileShape).thenIf(clickEnabled) { Modifier.combinedClickable( onClick = { onClick(it) }, onLongClick = { onLongClick(it) } ) Modifier.fillMaxHeight().aspectRatio(1f).thenIf(toggleClickSupported) { Modifier.clip(TileDefaults.TileShape) .background(colors.iconBackground, { 1f }) .combinedClickable(onClick = onClick, onLongClick = onLongClick) } ) { TileIcon( icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center) ) } TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center)) } // Labels Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) { Text( label, Loading Loading @@ -743,8 +739,20 @@ private object TileDefaults { val TileShape = CircleShape val IconTileWithLabelHeight = 140.dp /** An active tile without dual target uses the active color as background */ @Composable fun activeTileColors(): TileColors = TileColors( background = MaterialTheme.colorScheme.primary, iconBackground = MaterialTheme.colorScheme.primary, label = MaterialTheme.colorScheme.onPrimary, secondaryLabel = MaterialTheme.colorScheme.onPrimary, icon = MaterialTheme.colorScheme.onPrimary, ) /** An active tile with dual target only show the active color on the icon */ @Composable fun activeDualTargetTileColors(): TileColors = TileColors( background = MaterialTheme.colorScheme.surfaceVariant, iconBackground = MaterialTheme.colorScheme.primary, Loading Loading @@ -774,9 +782,15 @@ private object TileDefaults { ) @Composable fun getColorForState(state: Int): TileColors { return when (state) { STATE_ACTIVE -> activeTileColors() fun getColorForState(uiState: TileUiState): TileColors { return when (uiState.state) { STATE_ACTIVE -> { if (uiState.handlesSecondaryClick) { activeDualTargetTileColors() } else { activeTileColors() } } STATE_INACTIVE -> inactiveTileColors() else -> unavailableTileColors() } Loading packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt +2 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ data class TileUiState( val label: String, val secondaryLabel: String, val state: Int, val handlesSecondaryClick: Boolean, val icon: Supplier<QSTile.Icon?>, ) Loading @@ -33,6 +34,7 @@ fun QSTile.State.toUiState(): TileUiState { label?.toString() ?: "", secondaryLabel?.toString() ?: "", state, handlesSecondaryClick, icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null }, ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt +5 −1 Original line number Diff line number Diff line Loading @@ -117,7 +117,11 @@ class InternetTileMapperTest : SysuiTestCase() { label, activationState, secondaryLabel, setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), setOf( QSTileState.UserAction.CLICK, QSTileState.UserAction.TOGGLE_CLICK, QSTileState.UserAction.LONG_CLICK ), contentDescription, null, QSTileState.SideViewIcon.Chevron, Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt +26 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandl import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx import com.android.systemui.qs.tiles.dialog.InternetDialogManager import com.android.systemui.qs.tiles.dialog.WifiStateWorker import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel import com.android.systemui.statusbar.connectivity.AccessPointController import com.android.systemui.util.mockito.mock Loading @@ -40,6 +41,8 @@ import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.kotlin.times import org.mockito.kotlin.whenever @SmallTest @EnabledOnRavenwood Loading @@ -51,17 +54,20 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() { private lateinit var underTest: InternetTileUserActionInteractor @Mock private lateinit var internetDialogManager: InternetDialogManager @Mock private lateinit var wifiStateWorker: WifiStateWorker @Mock private lateinit var controller: AccessPointController @Before fun setup() { internetDialogManager = mock<InternetDialogManager>() wifiStateWorker = mock<WifiStateWorker>() controller = mock<AccessPointController>() underTest = InternetTileUserActionInteractor( kosmos.testScope.coroutineContext, internetDialogManager, wifiStateWorker, controller, inputHandler, ) Loading Loading @@ -110,4 +116,24 @@ class InternetTileUserActionInteractorTest : SysuiTestCase() { Truth.assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS) } } @Test fun handleSecondaryClickWhenWifiOn() = kosmos.testScope.runTest { whenever(wifiStateWorker.isWifiEnabled).thenReturn(true) underTest.handleInput(QSTileInputTestKtx.toggleClick(InternetTileModel.Active())) verify(wifiStateWorker, times(1)).isWifiEnabled = eq(false) } @Test fun handleSecondaryClickWhenWifiOff() = kosmos.testScope.runTest { whenever(wifiStateWorker.isWifiEnabled).thenReturn(false) underTest.handleInput(QSTileInputTestKtx.toggleClick(InternetTileModel.Inactive())) verify(wifiStateWorker, times(1)).isWifiEnabled = eq(true) } }
packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java +4 −0 Original line number Diff line number Diff line Loading @@ -169,6 +169,7 @@ public interface QSTile { public boolean isTransient = false; public String expandedAccessibilityClassName; public boolean handlesLongClick = true; public boolean handlesSecondaryClick = false; @Nullable public Drawable sideViewCustomDrawable; public String spec; Loading Loading @@ -212,6 +213,7 @@ public interface QSTile { || !Objects.equals(other.isTransient, isTransient) || !Objects.equals(other.dualTarget, dualTarget) || !Objects.equals(other.handlesLongClick, handlesLongClick) || !Objects.equals(other.handlesSecondaryClick, handlesSecondaryClick) || !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable); other.spec = spec; other.icon = icon; Loading @@ -227,6 +229,7 @@ public interface QSTile { other.dualTarget = dualTarget; other.isTransient = isTransient; other.handlesLongClick = handlesLongClick; other.handlesSecondaryClick = handlesSecondaryClick; other.sideViewCustomDrawable = sideViewCustomDrawable; return changed; } Loading @@ -252,6 +255,7 @@ public interface QSTile { sb.append(",disabledByPolicy=").append(disabledByPolicy); sb.append(",dualTarget=").append(dualTarget); sb.append(",isTransient=").append(isTransient); sb.append(",handlesSecondaryClick=").append(handlesSecondaryClick); sb.append(",state=").append(state); sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable); return sb.append(']'); Loading
packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt +45 −31 Original line number Diff line number Diff line Loading @@ -109,6 +109,7 @@ import com.android.systemui.qs.panels.ui.model.GridCell import com.android.systemui.qs.panels.ui.model.SpacerGridCell import com.android.systemui.qs.panels.ui.model.TileGridCell import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileUiState import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel import com.android.systemui.qs.panels.ui.viewmodel.toUiState import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor Loading @@ -129,7 +130,7 @@ fun Tile( ) { val state by tile.state.collectAsStateWithLifecycle(tile.currentState) val uiState = remember(state) { state.toUiState() } val colors = TileDefaults.getColorForState(uiState.state) val colors = TileDefaults.getColorForState(uiState) TileContainer( colors = colors, Loading @@ -150,9 +151,13 @@ fun Tile( secondaryLabel = uiState.secondaryLabel, icon = icon, colors = colors, clickEnabled = true, onClick = tile::onSecondaryClick, onLongClick = tile::onLongClick, toggleClickSupported = state.handlesSecondaryClick, onClick = { if (state.handlesSecondaryClick) { tile.onSecondaryClick() } }, onLongClick = { tile.onLongClick(it) }, ) } } Loading @@ -168,7 +173,7 @@ private fun TileContainer( onClick: (Expandable) -> Unit = {}, onLongClick: (Expandable) -> Unit = {}, modifier: Modifier = Modifier, content: @Composable BoxScope.() -> Unit, content: @Composable BoxScope.(Expandable) -> Unit, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, Loading Loading @@ -200,7 +205,7 @@ private fun TileContainer( } .tilePadding(), ) { content() content(it) } } Loading @@ -222,36 +227,27 @@ private fun LargeTileContent( secondaryLabel: String?, icon: Icon, colors: TileColors, clickEnabled: Boolean = false, onClick: (Expandable) -> Unit = {}, onLongClick: (Expandable) -> Unit = {}, toggleClickSupported: Boolean = false, onClick: () -> Unit = {}, onLongClick: () -> Unit = {}, ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = tileHorizontalArrangement() ) { Expandable( color = colors.iconBackground, shape = TileDefaults.TileShape, modifier = Modifier.fillMaxHeight().aspectRatio(1f) ) { // Icon Box( modifier = Modifier.fillMaxSize().clip(TileDefaults.TileShape).thenIf(clickEnabled) { Modifier.combinedClickable( onClick = { onClick(it) }, onLongClick = { onLongClick(it) } ) Modifier.fillMaxHeight().aspectRatio(1f).thenIf(toggleClickSupported) { Modifier.clip(TileDefaults.TileShape) .background(colors.iconBackground, { 1f }) .combinedClickable(onClick = onClick, onLongClick = onLongClick) } ) { TileIcon( icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center) ) } TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center)) } // Labels Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) { Text( label, Loading Loading @@ -743,8 +739,20 @@ private object TileDefaults { val TileShape = CircleShape val IconTileWithLabelHeight = 140.dp /** An active tile without dual target uses the active color as background */ @Composable fun activeTileColors(): TileColors = TileColors( background = MaterialTheme.colorScheme.primary, iconBackground = MaterialTheme.colorScheme.primary, label = MaterialTheme.colorScheme.onPrimary, secondaryLabel = MaterialTheme.colorScheme.onPrimary, icon = MaterialTheme.colorScheme.onPrimary, ) /** An active tile with dual target only show the active color on the icon */ @Composable fun activeDualTargetTileColors(): TileColors = TileColors( background = MaterialTheme.colorScheme.surfaceVariant, iconBackground = MaterialTheme.colorScheme.primary, Loading Loading @@ -774,9 +782,15 @@ private object TileDefaults { ) @Composable fun getColorForState(state: Int): TileColors { return when (state) { STATE_ACTIVE -> activeTileColors() fun getColorForState(uiState: TileUiState): TileColors { return when (uiState.state) { STATE_ACTIVE -> { if (uiState.handlesSecondaryClick) { activeDualTargetTileColors() } else { activeTileColors() } } STATE_INACTIVE -> inactiveTileColors() else -> unavailableTileColors() } Loading
packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt +2 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ data class TileUiState( val label: String, val secondaryLabel: String, val state: Int, val handlesSecondaryClick: Boolean, val icon: Supplier<QSTile.Icon?>, ) Loading @@ -33,6 +34,7 @@ fun QSTile.State.toUiState(): TileUiState { label?.toString() ?: "", secondaryLabel?.toString() ?: "", state, handlesSecondaryClick, icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null }, ) }