Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt +35 −0 Original line number Diff line number Diff line Loading @@ -146,6 +146,41 @@ class MutableSelectionStateTest : SysuiTestCase() { .isEqualTo(None) } @Test fun toggleSelection_correctlyUpdatesSelection() { // Select the first spec underTest.toggleSelection(TEST_SPEC) assertThat(underTest.selection).isEqualTo(TEST_SPEC) // Select a second spec underTest.toggleSelection(TEST_SPEC_2) assertThat(underTest.selection).isEqualTo(TEST_SPEC_2) // Toggle on the same spec and assert the selection is now null underTest.toggleSelection(TEST_SPEC_2) assertThat(underTest.selection).isNull() } @Test fun placeTileAt_onlyWhenPlacementEnabled() { // Without enabling placement mode, attempt a placement underTest.placeTileAt(TEST_SPEC) // Assert nothing happened assertThat(underTest.placementEvent).isNull() } @Test fun placeTileAt_createsCorrectPlacementEvent() { underTest.enterPlacementMode(TEST_SPEC) underTest.placeTileAt(TEST_SPEC_2) assertThat(underTest.placementEnabled).isFalse() val event = underTest.placementEvent as PlacementEvent.PlaceToTileSpec assertThat(event.movingSpec).isEqualTo(TEST_SPEC) assertThat(event.targetSpec).isEqualTo(TEST_SPEC_2) } companion object { private val TEST_SPEC = TileSpec.create("testSpec") private val TEST_SPEC_2 = TileSpec.create("testSpec2") Loading packages/SystemUI/res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -2645,6 +2645,9 @@ <!-- Accessibility description of action to remove QS tile on click. [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_remove_tile_action">Remove tile</string> <!-- Accessibility description of action to place the QS tile on click. [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_place_tile_action">Place tile</string> <!-- Accessibility description of action to select the QS tile to place on click. It will read as "Double-tap to toggle placement mode" in screen readers [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_toggle_placement_mode">toggle placement mode</string> Loading packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +30 −7 Original line number Diff line number Diff line Loading @@ -877,24 +877,47 @@ private fun LazyGridItemScope.TileGridCell( selectionState::unSelect, ) val toggleSelectionLabel = stringResource(R.string.accessibility_qs_edit_toggle_selection) val placeTileLabel = stringResource(R.string.accessibility_qs_edit_place_tile_action) Box( Modifier.fillMaxSize() .clearAndSetSemantics { this.stateDescription = stateDescription contentDescription = cell.tile.label.text customActions = listOf( // TODO(b/367748260): Add final accessibility actions val actions = mutableListOf( CustomAccessibilityAction(togglePlacementModeLabel) { selectionState.togglePlacementMode(cell.tile.tileSpec) true } ) if (selectionState.placementEnabled) { actions.add( CustomAccessibilityAction(placeTileLabel) { selectionState.placeTileAt(cell.tile.tileSpec) true } ) } else { // Don't allow for resizing during placement mode actions.add( CustomAccessibilityAction(toggleSizeLabel) { onResize(FinalResizeOperation(cell.tile.tileSpec, !cell.isIcon)) true }, CustomAccessibilityAction(togglePlacementModeLabel) { selectionState.togglePlacementMode(cell.tile.tileSpec) } ) actions.add( CustomAccessibilityAction(toggleSelectionLabel) { selectionState.toggleSelection(cell.tile.tileSpec) true }, } ) } customActions = actions } .selectableTile(cell.tile.tileSpec, selectionState) .thenIf(isReadyToDrag) { draggableModifier } .tileBackground { backgroundColor } Loading packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt +13 −6 Original line number Diff line number Diff line Loading @@ -63,6 +63,10 @@ class MutableSelectionState { exitPlacementMode() } fun toggleSelection(tileSpec: TileSpec) { if (selection == tileSpec) unSelect() else select(tileSpec) } /** Selects [tileSpec] and enable placement mode. */ fun enterPlacementMode(tileSpec: TileSpec) { selection = tileSpec Loading @@ -78,6 +82,13 @@ class MutableSelectionState { if (placementEnabled) exitPlacementMode() else enterPlacementMode(tileSpec) } fun placeTileAt(tileSpec: TileSpec) { if (!placementEnabled) return selection?.let { placementEvent = PlacementEvent.PlaceToTileSpec(it, tileSpec) } exitPlacementMode() } suspend fun tileStateFor( tileSpec: TileSpec, previousState: TileState, Loading Loading @@ -114,14 +125,10 @@ class MutableSelectionState { exitPlacementMode() } placementEnabled -> { selection?.let { placementEvent = PlacementEvent.PlaceToTileSpec(it, tileSpec) } exitPlacementMode() } selection == tileSpec -> { unSelect() placeTileAt(tileSpec) } else -> { select(tileSpec) toggleSelection(tileSpec) } } } Loading packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt +66 −1 Original line number Diff line number Diff line Loading @@ -22,12 +22,14 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Modifier import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.doubleClick import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performCustomAccessibilityActionWithLabel import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.text.AnnotatedString import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -43,11 +45,14 @@ import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridSnapshotViewModelFactory import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.res.R import com.android.systemui.testKosmos import org.junit.Assert.assertThrows import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @OptIn(ExperimentalTestApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class EditModeTest : SysuiTestCase() { Loading @@ -59,10 +64,13 @@ class EditModeTest : SysuiTestCase() { @Composable private fun EditTileGridUnderTest() { val allTiles = remember { TestEditTiles.toMutableStateList() } val largeTiles = remember { TestLargeTilesSpecs.toMutableStateList() } val currentTiles = allTiles.filter { it.isCurrent } val listState = EditTileListState(currentTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2) LaunchedEffect(currentTiles) { listState.updateTiles(currentTiles, TestLargeTilesSpecs) } LaunchedEffect(currentTiles, largeTiles) { listState.updateTiles(currentTiles, largeTiles.toSet()) } val snapshotViewModel = remember { snapshotViewModelFactory.create() } Loading Loading @@ -92,6 +100,13 @@ class EditModeTest : SysuiTestCase() { val index = allTiles.indexOfFirst { it.tileSpec == action.tileSpec } allTiles[index] = allTiles[index].copy(isCurrent = false) } is EditAction.ResizeTile -> { if (action.toIcon) { largeTiles.remove(action.tileSpec) } else { largeTiles.add(action.tileSpec) } } else -> error("Not expecting action $action from test") } } Loading Loading @@ -128,6 +143,56 @@ class EditModeTest : SysuiTestCase() { ) } @Test fun resizingAction_dependsOnPlacementMode() { composeRule.setContent { EditTileGridUnderTest() } composeRule.waitForIdle() // Use the toggle size action composeRule .onNodeWithContentDescription("tileE") .performCustomAccessibilityActionWithLabel( context.getString(R.string.accessibility_qs_edit_toggle_tile_size_action) ) // Double tap "tileA" to enable placement mode composeRule.onNodeWithContentDescription("tileA").performTouchInput { doubleClick() } // Assert the toggle size action is missing assertThrows(AssertionError::class.java) { composeRule .onNodeWithContentDescription("tileE") .performCustomAccessibilityActionWithLabel( context.getString(R.string.accessibility_qs_edit_toggle_tile_size_action) ) } } @Test fun placementAction_dependsOnPlacementMode() { composeRule.setContent { EditTileGridUnderTest() } composeRule.waitForIdle() // Assert the placement action is missing assertThrows(AssertionError::class.java) { composeRule .onNodeWithContentDescription("tileE") .performCustomAccessibilityActionWithLabel( context.getString(R.string.accessibility_qs_edit_place_tile_action) ) } // Double tap "tileA" to enable placement mode composeRule.onNodeWithContentDescription("tileA").performTouchInput { doubleClick() } // Use the placement action composeRule .onNodeWithContentDescription("tileE") .performCustomAccessibilityActionWithLabel( context.getString(R.string.accessibility_qs_edit_place_tile_action) ) } @Test fun performAction_undoAppears() { composeRule.setContent { EditTileGridUnderTest() } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt +35 −0 Original line number Diff line number Diff line Loading @@ -146,6 +146,41 @@ class MutableSelectionStateTest : SysuiTestCase() { .isEqualTo(None) } @Test fun toggleSelection_correctlyUpdatesSelection() { // Select the first spec underTest.toggleSelection(TEST_SPEC) assertThat(underTest.selection).isEqualTo(TEST_SPEC) // Select a second spec underTest.toggleSelection(TEST_SPEC_2) assertThat(underTest.selection).isEqualTo(TEST_SPEC_2) // Toggle on the same spec and assert the selection is now null underTest.toggleSelection(TEST_SPEC_2) assertThat(underTest.selection).isNull() } @Test fun placeTileAt_onlyWhenPlacementEnabled() { // Without enabling placement mode, attempt a placement underTest.placeTileAt(TEST_SPEC) // Assert nothing happened assertThat(underTest.placementEvent).isNull() } @Test fun placeTileAt_createsCorrectPlacementEvent() { underTest.enterPlacementMode(TEST_SPEC) underTest.placeTileAt(TEST_SPEC_2) assertThat(underTest.placementEnabled).isFalse() val event = underTest.placementEvent as PlacementEvent.PlaceToTileSpec assertThat(event.movingSpec).isEqualTo(TEST_SPEC) assertThat(event.targetSpec).isEqualTo(TEST_SPEC_2) } companion object { private val TEST_SPEC = TileSpec.create("testSpec") private val TEST_SPEC_2 = TileSpec.create("testSpec2") Loading
packages/SystemUI/res/values/strings.xml +3 −0 Original line number Diff line number Diff line Loading @@ -2645,6 +2645,9 @@ <!-- Accessibility description of action to remove QS tile on click. [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_remove_tile_action">Remove tile</string> <!-- Accessibility description of action to place the QS tile on click. [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_place_tile_action">Place tile</string> <!-- Accessibility description of action to select the QS tile to place on click. It will read as "Double-tap to toggle placement mode" in screen readers [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_toggle_placement_mode">toggle placement mode</string> Loading
packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +30 −7 Original line number Diff line number Diff line Loading @@ -877,24 +877,47 @@ private fun LazyGridItemScope.TileGridCell( selectionState::unSelect, ) val toggleSelectionLabel = stringResource(R.string.accessibility_qs_edit_toggle_selection) val placeTileLabel = stringResource(R.string.accessibility_qs_edit_place_tile_action) Box( Modifier.fillMaxSize() .clearAndSetSemantics { this.stateDescription = stateDescription contentDescription = cell.tile.label.text customActions = listOf( // TODO(b/367748260): Add final accessibility actions val actions = mutableListOf( CustomAccessibilityAction(togglePlacementModeLabel) { selectionState.togglePlacementMode(cell.tile.tileSpec) true } ) if (selectionState.placementEnabled) { actions.add( CustomAccessibilityAction(placeTileLabel) { selectionState.placeTileAt(cell.tile.tileSpec) true } ) } else { // Don't allow for resizing during placement mode actions.add( CustomAccessibilityAction(toggleSizeLabel) { onResize(FinalResizeOperation(cell.tile.tileSpec, !cell.isIcon)) true }, CustomAccessibilityAction(togglePlacementModeLabel) { selectionState.togglePlacementMode(cell.tile.tileSpec) } ) actions.add( CustomAccessibilityAction(toggleSelectionLabel) { selectionState.toggleSelection(cell.tile.tileSpec) true }, } ) } customActions = actions } .selectableTile(cell.tile.tileSpec, selectionState) .thenIf(isReadyToDrag) { draggableModifier } .tileBackground { backgroundColor } Loading
packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt +13 −6 Original line number Diff line number Diff line Loading @@ -63,6 +63,10 @@ class MutableSelectionState { exitPlacementMode() } fun toggleSelection(tileSpec: TileSpec) { if (selection == tileSpec) unSelect() else select(tileSpec) } /** Selects [tileSpec] and enable placement mode. */ fun enterPlacementMode(tileSpec: TileSpec) { selection = tileSpec Loading @@ -78,6 +82,13 @@ class MutableSelectionState { if (placementEnabled) exitPlacementMode() else enterPlacementMode(tileSpec) } fun placeTileAt(tileSpec: TileSpec) { if (!placementEnabled) return selection?.let { placementEvent = PlacementEvent.PlaceToTileSpec(it, tileSpec) } exitPlacementMode() } suspend fun tileStateFor( tileSpec: TileSpec, previousState: TileState, Loading Loading @@ -114,14 +125,10 @@ class MutableSelectionState { exitPlacementMode() } placementEnabled -> { selection?.let { placementEvent = PlacementEvent.PlaceToTileSpec(it, tileSpec) } exitPlacementMode() } selection == tileSpec -> { unSelect() placeTileAt(tileSpec) } else -> { select(tileSpec) toggleSelection(tileSpec) } } } Loading
packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt +66 −1 Original line number Diff line number Diff line Loading @@ -22,12 +22,14 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Modifier import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.doubleClick import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performCustomAccessibilityActionWithLabel import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.text.AnnotatedString import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -43,11 +45,14 @@ import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridSnapshotViewModelFactory import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.res.R import com.android.systemui.testKosmos import org.junit.Assert.assertThrows import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @OptIn(ExperimentalTestApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class EditModeTest : SysuiTestCase() { Loading @@ -59,10 +64,13 @@ class EditModeTest : SysuiTestCase() { @Composable private fun EditTileGridUnderTest() { val allTiles = remember { TestEditTiles.toMutableStateList() } val largeTiles = remember { TestLargeTilesSpecs.toMutableStateList() } val currentTiles = allTiles.filter { it.isCurrent } val listState = EditTileListState(currentTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2) LaunchedEffect(currentTiles) { listState.updateTiles(currentTiles, TestLargeTilesSpecs) } LaunchedEffect(currentTiles, largeTiles) { listState.updateTiles(currentTiles, largeTiles.toSet()) } val snapshotViewModel = remember { snapshotViewModelFactory.create() } Loading Loading @@ -92,6 +100,13 @@ class EditModeTest : SysuiTestCase() { val index = allTiles.indexOfFirst { it.tileSpec == action.tileSpec } allTiles[index] = allTiles[index].copy(isCurrent = false) } is EditAction.ResizeTile -> { if (action.toIcon) { largeTiles.remove(action.tileSpec) } else { largeTiles.add(action.tileSpec) } } else -> error("Not expecting action $action from test") } } Loading Loading @@ -128,6 +143,56 @@ class EditModeTest : SysuiTestCase() { ) } @Test fun resizingAction_dependsOnPlacementMode() { composeRule.setContent { EditTileGridUnderTest() } composeRule.waitForIdle() // Use the toggle size action composeRule .onNodeWithContentDescription("tileE") .performCustomAccessibilityActionWithLabel( context.getString(R.string.accessibility_qs_edit_toggle_tile_size_action) ) // Double tap "tileA" to enable placement mode composeRule.onNodeWithContentDescription("tileA").performTouchInput { doubleClick() } // Assert the toggle size action is missing assertThrows(AssertionError::class.java) { composeRule .onNodeWithContentDescription("tileE") .performCustomAccessibilityActionWithLabel( context.getString(R.string.accessibility_qs_edit_toggle_tile_size_action) ) } } @Test fun placementAction_dependsOnPlacementMode() { composeRule.setContent { EditTileGridUnderTest() } composeRule.waitForIdle() // Assert the placement action is missing assertThrows(AssertionError::class.java) { composeRule .onNodeWithContentDescription("tileE") .performCustomAccessibilityActionWithLabel( context.getString(R.string.accessibility_qs_edit_place_tile_action) ) } // Double tap "tileA" to enable placement mode composeRule.onNodeWithContentDescription("tileA").performTouchInput { doubleClick() } // Use the placement action composeRule .onNodeWithContentDescription("tileE") .performCustomAccessibilityActionWithLabel( context.getString(R.string.accessibility_qs_edit_place_tile_action) ) } @Test fun performAction_undoAppears() { composeRule.setContent { EditTileGridUnderTest() } Loading