1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.qs.panels.ui.compose 18 19 import androidx.compose.foundation.layout.fillMaxSize 20 import androidx.compose.runtime.Composable 21 import androidx.compose.runtime.getValue 22 import androidx.compose.runtime.mutableStateOf 23 import androidx.compose.runtime.setValue 24 import androidx.compose.ui.Modifier 25 import androidx.compose.ui.test.ExperimentalTestApi 26 import androidx.compose.ui.test.click 27 import androidx.compose.ui.test.junit4.createComposeRule 28 import androidx.compose.ui.test.onNodeWithContentDescription 29 import androidx.compose.ui.test.performClick 30 import androidx.compose.ui.test.performCustomAccessibilityActionWithLabel 31 import androidx.compose.ui.test.performTouchInput 32 import androidx.compose.ui.test.swipeLeft 33 import androidx.compose.ui.test.swipeRight 34 import androidx.compose.ui.text.AnnotatedString 35 import androidx.test.ext.junit.runners.AndroidJUnit4 36 import androidx.test.filters.SmallTest 37 import com.android.systemui.SysuiTestCase 38 import com.android.systemui.common.shared.model.ContentDescription 39 import com.android.systemui.common.shared.model.Icon 40 import com.android.systemui.qs.panels.shared.model.SizedTile 41 import com.android.systemui.qs.panels.shared.model.SizedTileImpl 42 import com.android.systemui.qs.panels.ui.compose.infinitegrid.DefaultEditTileGrid 43 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel 44 import com.android.systemui.qs.pipeline.shared.TileSpec 45 import com.android.systemui.qs.shared.model.TileCategory 46 import com.google.common.truth.Truth.assertThat 47 import org.junit.Rule 48 import org.junit.Test 49 import org.junit.runner.RunWith 50 51 @OptIn(ExperimentalTestApi::class) 52 @SmallTest 53 @RunWith(AndroidJUnit4::class) 54 class ResizingTest : SysuiTestCase() { 55 @get:Rule val composeRule = createComposeRule() 56 57 @Composable 58 private fun EditTileGridUnderTest( 59 listState: EditTileListState, 60 onResize: (TileSpec, Boolean) -> Unit, 61 ) { 62 DefaultEditTileGrid( 63 listState = listState, 64 otherTiles = listOf(), 65 columns = 4, 66 largeTilesSpan = 4, 67 modifier = Modifier.fillMaxSize(), 68 onRemoveTile = {}, 69 onSetTiles = {}, 70 onResize = onResize, 71 onStopEditing = {}, 72 onReset = null, 73 ) 74 } 75 76 @Test 77 fun toggleIconTileWithA11yAction_shouldBeLarge() { 78 var tiles by mutableStateOf(TestEditTiles) 79 val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) 80 composeRule.setContent { 81 EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } 82 } 83 composeRule.waitForIdle() 84 85 composeRule 86 .onNodeWithContentDescription("tileA") 87 .performCustomAccessibilityActionWithLabel("Toggle size") 88 89 assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(2) 90 } 91 92 @Test 93 fun toggleLargeTileWithA11yAction_shouldBeIcon() { 94 var tiles by mutableStateOf(TestEditTiles) 95 val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) 96 composeRule.setContent { 97 EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } 98 } 99 composeRule.waitForIdle() 100 101 composeRule 102 .onNodeWithContentDescription("tileD_large") 103 .performCustomAccessibilityActionWithLabel("Toggle size") 104 105 assertThat(tiles.find { it.tile.tileSpec.spec == "tileD_large" }?.width).isEqualTo(1) 106 } 107 108 @Test 109 fun tapOnIconResizingHandle_shouldBeLarge() { 110 var tiles by mutableStateOf(TestEditTiles) 111 val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) 112 composeRule.setContent { 113 EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } 114 } 115 composeRule.waitForIdle() 116 117 composeRule 118 .onNodeWithContentDescription("tileA") 119 .performClick() // Select 120 .performTouchInput { // Tap on resizing handle 121 click(centerRight) 122 } 123 composeRule.waitForIdle() 124 125 assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(2) 126 } 127 128 @Test 129 fun tapOnLargeResizingHandle_shouldBeIcon() { 130 var tiles by mutableStateOf(TestEditTiles) 131 val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) 132 composeRule.setContent { 133 EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } 134 } 135 composeRule.waitForIdle() 136 137 composeRule 138 .onNodeWithContentDescription("tileD_large") 139 .performClick() // Select 140 .performTouchInput { // Tap on resizing handle 141 click(centerRight) 142 } 143 composeRule.waitForIdle() 144 145 assertThat(tiles.find { it.tile.tileSpec.spec == "tileD_large" }?.width).isEqualTo(1) 146 } 147 148 @Test 149 fun resizedIcon_shouldBeLarge() { 150 var tiles by mutableStateOf(TestEditTiles) 151 val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) 152 composeRule.setContent { 153 EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } 154 } 155 composeRule.waitForIdle() 156 157 composeRule 158 .onNodeWithContentDescription("tileA") 159 .performClick() // Select 160 .performTouchInput { // Resize up 161 swipeRight(startX = right, endX = right * 2) 162 } 163 composeRule.waitForIdle() 164 165 assertThat(tiles.find { it.tile.tileSpec.spec == "tileA" }?.width).isEqualTo(2) 166 } 167 168 @Test 169 fun resizedLarge_shouldBeIcon() { 170 var tiles by mutableStateOf(TestEditTiles) 171 val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) 172 composeRule.setContent { 173 EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } 174 } 175 composeRule.waitForIdle() 176 177 composeRule 178 .onNodeWithContentDescription("tileD_large") 179 .performClick() // Select 180 .performTouchInput { // Resize down 181 swipeLeft() 182 } 183 composeRule.waitForIdle() 184 185 assertThat(tiles.find { it.tile.tileSpec.spec == "tileD_large" }?.width).isEqualTo(1) 186 } 187 188 companion object { 189 private fun List<SizedTile<EditTileViewModel>>.resize( 190 spec: TileSpec, 191 toIcon: Boolean, 192 ): List<SizedTile<EditTileViewModel>> { 193 return map { 194 if (it.tile.tileSpec == spec) { 195 SizedTileImpl(it.tile, width = if (toIcon) 1 else 2) 196 } else { 197 it 198 } 199 } 200 } 201 202 private fun createEditTile(tileSpec: String): SizedTile<EditTileViewModel> { 203 return SizedTileImpl( 204 EditTileViewModel( 205 tileSpec = TileSpec.create(tileSpec), 206 icon = 207 Icon.Resource( 208 android.R.drawable.star_on, 209 ContentDescription.Loaded(tileSpec), 210 ), 211 label = AnnotatedString(tileSpec), 212 appName = null, 213 isCurrent = true, 214 availableEditActions = emptySet(), 215 category = TileCategory.UNKNOWN, 216 ), 217 getWidth(tileSpec), 218 ) 219 } 220 221 private fun getWidth(tileSpec: String): Int { 222 return if (tileSpec.endsWith("large")) { 223 2 224 } else { 225 1 226 } 227 } 228 229 private val TestEditTiles = 230 listOf( 231 createEditTile("tileA"), 232 createEditTile("tileB"), 233 createEditTile("tileC"), 234 createEditTile("tileD_large"), 235 createEditTile("tileE"), 236 ) 237 } 238 } 239