Merge "Implement top app bar and reset button for Edit mode" into main
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/DynamicIconTilesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/DynamicIconTilesInteractorTest.kt
new file mode 100644
index 0000000..75d4b91
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/DynamicIconTilesInteractorTest.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
+import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
+import com.android.systemui.qs.panels.data.repository.qsPreferencesRepository
+import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DynamicIconTilesInteractorTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ defaultLargeTilesRepository =
+ object : DefaultLargeTilesRepository {
+ override val defaultLargeTiles: Set<TileSpec> = setOf(largeTile)
+ }
+ currentTilesInteractor.setTiles(listOf(largeTile, smallTile))
+ }
+ private lateinit var underTest: DynamicIconTilesInteractor
+
+ @Before
+ fun setUp() {
+ with(kosmos) {
+ underTest = dynamicIconTilesInteractorFactory.create()
+ underTest.activateIn(testScope)
+ }
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun removingTile_updatesSharedPreferences() =
+ with(kosmos) {
+ testScope.runTest {
+ val latest by collectLastValue(qsPreferencesRepository.largeTilesSpecs)
+ runCurrent()
+
+ // Remove the large tile from the current tiles
+ currentTilesInteractor.removeTiles(listOf(largeTile))
+ runCurrent()
+
+ // Assert that it resized to small
+ assertThat(latest).doesNotContain(largeTile)
+ }
+ }
+
+ private companion object {
+ private val largeTile = TileSpec.create("large")
+ private val smallTile = TileSpec.create("small")
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
index 79a303d..ed28dc8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
@@ -98,23 +98,6 @@
@OptIn(ExperimentalCoroutinesApi::class)
@Test
- fun removingTile_updatesSharedPreferences() =
- with(kosmos) {
- testScope.runTest {
- val latest by collectLastValue(qsPreferencesRepository.largeTilesSpecs)
- runCurrent()
-
- // Remove the large tile from the current tiles
- currentTilesInteractor.removeTiles(listOf(largeTile))
- runCurrent()
-
- // Assert that it resized to small
- assertThat(latest).doesNotContain(largeTile)
- }
- }
-
- @OptIn(ExperimentalCoroutinesApi::class)
- @Test
fun resizingNonCurrentTile_doesNothing() =
with(kosmos) {
testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/SizedTilesResetInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/SizedTilesResetInteractorTest.kt
new file mode 100644
index 0000000..ee7a15e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/SizedTilesResetInteractorTest.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
+import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.FakeDefaultTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeDefaultTilesRepository
+import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SizedTilesResetInteractorTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply {
+ defaultLargeTilesRepository =
+ object : DefaultLargeTilesRepository {
+ override val defaultLargeTiles: Set<TileSpec> = setOf(largeTile)
+ }
+ fakeDefaultTilesRepository = FakeDefaultTilesRepository(listOf(smallTile, largeTile))
+ }
+ private val underTest = with(kosmos) { sizedTilesResetInteractor }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun changeTiles_resetsCorrectly() {
+ with(kosmos) {
+ testScope.runTest {
+ // Change current tiles and large tiles
+ currentTilesInteractor.setTiles(listOf(largeTile, newTile))
+ iconTilesInteractor.setLargeTiles(setOf(newTile))
+ runCurrent()
+
+ // Assert both current tiles and large tiles changed
+ assertThat(currentTilesInteractor.currentTilesSpecs)
+ .containsExactly(largeTile, newTile)
+ assertThat(iconTilesInteractor.largeTilesSpecs.value).containsExactly(newTile)
+
+ // Reset to default
+ underTest.reset()
+ runCurrent()
+
+ // Assert both current tiles and large tiles are back to the initial state
+ assertThat(currentTilesInteractor.currentTilesSpecs)
+ .containsExactly(largeTile, smallTile)
+ assertThat(iconTilesInteractor.largeTilesSpecs.value).containsExactly(largeTile)
+ }
+ }
+ }
+
+ private companion object {
+ private val largeTile = TileSpec.create("large")
+ private val smallTile = TileSpec.create("small")
+ private val newTile = TileSpec.create("newTile")
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 7ebebd7..23056b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -91,7 +91,7 @@
context.resources,
logger,
retailModeRepository,
- userTileSpecRepositoryFactory
+ userTileSpecRepositoryFactory,
)
}
@@ -218,6 +218,21 @@
assertThat(loadTilesForUser(user)).isEqualTo(startingTiles)
}
+ @Test
+ fun resetsDefault() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tilesSpecs(0))
+
+ val startingTiles = listOf(TileSpec.create("e"), TileSpec.create("f"))
+
+ underTest.setTiles(0, startingTiles)
+ runCurrent()
+
+ underTest.resetToDefault(0)
+
+ assertThat(tiles!!).containsExactlyElementsIn(DEFAULT_TILES.toTileSpecs())
+ }
+
private fun TestScope.storeTilesForUser(specs: String, forUser: Int) {
secureSettings.putStringForUser(SETTING, specs, forUser)
runCurrent()
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2c5fb56..cdf15ca 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3895,4 +3895,12 @@
<string name="qs_edit_mode_category_unknown">
Unknown
</string>
+ <!-- Title for the Reset Tiles dialog in QS Edit mode. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_reset_dialog_title">
+ Reset tiles
+ </string>
+ <!-- Content of the Reset Tiles dialog in QS Edit mode. [CHAR LIMIT=NONE] -->
+ <string name="qs_edit_mode_reset_dialog_content">
+ Reset tiles to their original order and sizes?
+ </string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 31e867e..ef30cbf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -23,6 +23,8 @@
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepositoryImpl
import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepositoryImpl
+import com.android.systemui.qs.panels.domain.interactor.EditTilesResetInteractor
+import com.android.systemui.qs.panels.domain.interactor.SizedTilesResetInteractor
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
import com.android.systemui.qs.panels.shared.model.PaginatedGridLayoutType
@@ -53,6 +55,9 @@
@Binds
fun bindGridLayoutTypeRepository(impl: GridLayoutTypeRepositoryImpl): GridLayoutTypeRepository
+ @Binds
+ fun bindEditTilesResetInteractor(impl: SizedTilesResetInteractor): EditTilesResetInteractor
+
@Binds fun bindIconTilesViewModel(impl: IconTilesViewModelImpl): IconTilesViewModel
@Binds fun bindQSColumnsViewModel(impl: QSColumnsSizeViewModelImpl): QSColumnsViewModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/DynamicIconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/DynamicIconTilesInteractor.kt
new file mode 100644
index 0000000..ee38dfb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/DynamicIconTilesInteractor.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** Interactor to resize QS tiles down to icons when removed from the current tiles. */
+class DynamicIconTilesInteractor
+@AssistedInject
+constructor(
+ private val iconTilesInteractor: IconTilesInteractor,
+ private val currentTilesInteractor: CurrentTilesInteractor,
+) : ExclusiveActivatable() {
+
+ override suspend fun onActivated(): Nothing {
+ currentTilesInteractor.currentTiles.collect { currentTiles ->
+ // Only current tiles can be resized, so observe the current tiles and find the
+ // intersection between them and the large tiles.
+ val newLargeTiles =
+ iconTilesInteractor.largeTilesSpecs.value intersect
+ currentTiles.map { it.spec }.toSet()
+ iconTilesInteractor.setLargeTiles(newLargeTiles)
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): DynamicIconTilesInteractor
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesResetInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesResetInteractor.kt
new file mode 100644
index 0000000..b523897
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesResetInteractor.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+/** Interactor for resetting QS tiles to the default state. */
+interface EditTilesResetInteractor {
+ fun reset()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
index fc59a50..ec61a0d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
@@ -27,7 +27,6 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
@@ -36,34 +35,27 @@
class IconTilesInteractor
@Inject
constructor(
- repo: DefaultLargeTilesRepository,
+ private val repo: DefaultLargeTilesRepository,
private val currentTilesInteractor: CurrentTilesInteractor,
private val preferencesInteractor: QSPreferencesInteractor,
@PanelsLog private val logBuffer: LogBuffer,
@Application private val applicationScope: CoroutineScope,
) {
-
val largeTilesSpecs =
- combine(preferencesInteractor.largeTilesSpecs, currentTilesInteractor.currentTiles) {
- largeTiles,
- currentTiles ->
- if (currentTiles.isEmpty()) {
- largeTiles
- } else {
- // Only current tiles can be resized, so observe the current tiles and find the
- // intersection between them and the large tiles.
- val newLargeTiles = largeTiles intersect currentTiles.map { it.spec }.toSet()
- if (newLargeTiles != largeTiles) {
- preferencesInteractor.setLargeTilesSpecs(newLargeTiles)
- }
- newLargeTiles
- }
- }
+ preferencesInteractor.largeTilesSpecs
.onEach { logChange(it) }
.stateIn(applicationScope, SharingStarted.Eagerly, repo.defaultLargeTiles)
fun isIconTile(spec: TileSpec): Boolean = !largeTilesSpecs.value.contains(spec)
+ fun setLargeTiles(specs: Set<TileSpec>) {
+ preferencesInteractor.setLargeTilesSpecs(specs)
+ }
+
+ fun resetToDefault() {
+ preferencesInteractor.setLargeTilesSpecs(repo.defaultLargeTiles)
+ }
+
fun resize(spec: TileSpec, toIcon: Boolean) {
if (!isCurrent(spec)) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/SizedTilesResetInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/SizedTilesResetInteractor.kt
new file mode 100644
index 0000000..a402587
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/SizedTilesResetInteractor.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.QSEditEvent
+import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import javax.inject.Inject
+
+/**
+ * This implementation of [EditTilesResetInteractor] resets both the current tiles and the sizes to
+ * the default state.
+ */
+@SysUISingleton
+class SizedTilesResetInteractor
+@Inject
+constructor(
+ private val currentTilesInteractor: CurrentTilesInteractor,
+ private val iconTilesInteractor: IconTilesInteractor,
+ private val uiEventLogger: UiEventLogger,
+) : EditTilesResetInteractor {
+ override fun reset() {
+ uiEventLogger.log(QSEditEvent.QS_EDIT_RESET)
+ currentTilesInteractor.resetTiles()
+ iconTilesInteractor.resetToDefault()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
index 1674865..e990d9d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
@@ -26,10 +26,7 @@
import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
@Composable
-fun EditMode(
- viewModel: EditModeViewModel,
- modifier: Modifier = Modifier,
-) {
+fun EditMode(viewModel: EditModeViewModel, modifier: Modifier = Modifier) {
val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
val tiles by viewModel.tiles.collectAsStateWithLifecycle(emptyList())
@@ -44,6 +41,7 @@
viewModel::addTile,
viewModel::removeTile,
viewModel::setTiles,
+ viewModel::stopEditing,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
index 0c02b40..0d37581 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
@@ -41,6 +41,7 @@
onAddTile: (TileSpec, Int) -> Unit,
onRemoveTile: (TileSpec) -> Unit,
onSetTiles: (List<TileSpec>) -> Unit,
+ onStopEditing: () -> Unit,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index 5c2a2bd..d2ec958 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -53,11 +53,19 @@
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Clear
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
@@ -133,6 +141,31 @@
object TileType
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
+ TopAppBar(
+ colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Black),
+ title = { Text(text = stringResource(id = R.string.qs_edit)) },
+ navigationIcon = {
+ IconButton(onClick = onStopEditing) {
+ Icon(
+ Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription =
+ stringResource(id = com.android.internal.R.string.action_bar_up_description),
+ )
+ }
+ },
+ actions = {
+ if (onReset != null) {
+ TextButton(onClick = onReset) {
+ Text(stringResource(id = com.android.internal.R.string.reset))
+ }
+ }
+ },
+ )
+}
+
@Composable
fun DefaultEditTileGrid(
listState: EditTileListState,
@@ -142,6 +175,8 @@
onRemoveTile: (TileSpec) -> Unit,
onSetTiles: (List<TileSpec>) -> Unit,
onResize: (TileSpec, toIcon: Boolean) -> Unit,
+ onStopEditing: () -> Unit,
+ onReset: (() -> Unit)?,
) {
val currentListState by rememberUpdatedState(listState)
val selectionState =
@@ -152,53 +187,71 @@
currentListState.isIcon(spec)?.let { onResize(spec, it) }
},
)
+ val reset: (() -> Unit)? =
+ if (onReset != null) {
+ {
+ selectionState.unSelect()
+ onReset()
+ }
+ } else {
+ null
+ }
- CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
- Column(
- verticalArrangement =
- spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
- modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState()),
- ) {
- AnimatedContent(
- targetState = listState.dragInProgress,
- modifier = Modifier.wrapContentSize(),
- label = "",
- ) { dragIsInProgress ->
- EditGridHeader(Modifier.dragAndDropRemoveZone(listState, onRemoveTile)) {
- if (dragIsInProgress) {
- RemoveTileTarget()
- } else {
- Text(text = "Hold and drag to rearrange tiles.")
+ Scaffold(
+ containerColor = Color.Transparent,
+ topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) },
+ ) { innerPadding ->
+ CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
+ Column(
+ verticalArrangement =
+ spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
+ modifier =
+ modifier
+ .fillMaxSize()
+ .verticalScroll(rememberScrollState())
+ .padding(innerPadding),
+ ) {
+ AnimatedContent(
+ targetState = listState.dragInProgress,
+ modifier = Modifier.wrapContentSize(),
+ label = "",
+ ) { dragIsInProgress ->
+ EditGridHeader(Modifier.dragAndDropRemoveZone(listState, onRemoveTile)) {
+ if (dragIsInProgress) {
+ RemoveTileTarget()
+ } else {
+ Text(text = "Hold and drag to rearrange tiles.")
+ }
}
}
- }
- CurrentTilesGrid(listState, selectionState, columns, onResize, onSetTiles)
+ CurrentTilesGrid(listState, selectionState, columns, onResize, onSetTiles)
- // Hide available tiles when dragging
- AnimatedVisibility(
- visible = !listState.dragInProgress,
- enter = fadeIn(),
- exit = fadeOut(),
- ) {
- Column(
- verticalArrangement =
- spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
- modifier = modifier.fillMaxSize(),
+ // Hide available tiles when dragging
+ AnimatedVisibility(
+ visible = !listState.dragInProgress,
+ enter = fadeIn(),
+ exit = fadeOut(),
) {
- EditGridHeader { Text(text = "Hold and drag to add tiles.") }
+ Column(
+ verticalArrangement =
+ spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
+ modifier = modifier.fillMaxSize(),
+ ) {
+ EditGridHeader { Text(text = "Hold and drag to add tiles.") }
- AvailableTileGrid(otherTiles, selectionState, columns, listState)
+ AvailableTileGrid(otherTiles, selectionState, columns, listState)
+ }
}
- }
- // Drop zone to remove tiles dragged out of the tile grid
- Spacer(
- modifier =
- Modifier.fillMaxWidth()
- .weight(1f)
- .dragAndDropRemoveZone(listState, onRemoveTile)
- )
+ // Drop zone to remove tiles dragged out of the tile grid
+ Spacer(
+ modifier =
+ Modifier.fillMaxWidth()
+ .weight(1f)
+ .dragAndDropRemoveZone(listState, onRemoveTile)
+ )
+ }
}
}
}
@@ -269,7 +322,7 @@
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
- shape = RoundedCornerShape(48.dp),
+ shape = RoundedCornerShape((TileHeight / 2) + CurrentTilesGridPadding),
)
.dragAndDropTileList(gridState, { gridContentOffset }, listState) { spec ->
onSetTiles(currentListState.tileSpecs())
@@ -313,10 +366,7 @@
text = category.label.load() ?: "",
fontSize = 20.sp,
color = labelColors.label,
- modifier =
- Modifier.fillMaxWidth()
- .background(Color.Black)
- .padding(start = 16.dp, bottom = 8.dp, top = 8.dp),
+ modifier = Modifier.fillMaxWidth().padding(start = 16.dp, bottom = 8.dp, top = 8.dp),
)
tiles.chunked(columns).forEach { row ->
Row(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index e5c2135..366bc9a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -28,6 +28,7 @@
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
+import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout
import com.android.systemui.qs.panels.ui.compose.bounceableInfo
@@ -35,8 +36,7 @@
import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.TileSquishinessViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey
@@ -48,8 +48,7 @@
@Inject
constructor(
private val iconTilesViewModel: IconTilesViewModel,
- private val gridSizeViewModel: QSColumnsViewModel,
- private val squishinessViewModel: TileSquishinessViewModel,
+ private val viewModelFactory: InfiniteGridViewModel.Factory,
) : PaginatableGridLayout {
@Composable
@@ -63,11 +62,19 @@
tiles.forEach { it.startListening(token) }
onDispose { tiles.forEach { it.stopListening(token) } }
}
- val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
+ val viewModel =
+ rememberViewModel(traceName = "InfiniteGridLayout.TileGrid") {
+ viewModelFactory.create()
+ }
+ val iconTilesViewModel =
+ rememberViewModel(traceName = "InfiniteGridLayout.TileGrid") {
+ viewModel.dynamicIconTilesViewModelFactory.create()
+ }
+ val columns by viewModel.gridSizeViewModel.columns.collectAsStateWithLifecycle()
val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) }
val bounceables =
remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } }
- val squishiness by squishinessViewModel.squishiness.collectAsStateWithLifecycle()
+ val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle()
val scope = rememberCoroutineScope()
var cellIndex = 0
@@ -98,8 +105,17 @@
onAddTile: (TileSpec, Int) -> Unit,
onRemoveTile: (TileSpec) -> Unit,
onSetTiles: (List<TileSpec>) -> Unit,
+ onStopEditing: () -> Unit,
) {
- val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
+ val viewModel =
+ rememberViewModel(traceName = "InfiniteGridLayout.EditTileGrid") {
+ viewModelFactory.create()
+ }
+ val iconTilesViewModel =
+ rememberViewModel(traceName = "InfiniteGridLayout.EditTileGrid") {
+ viewModel.dynamicIconTilesViewModelFactory.create()
+ }
+ val columns by viewModel.gridSizeViewModel.columns.collectAsStateWithLifecycle()
val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle()
// Non-current tiles should always be displayed as icon tiles.
@@ -123,6 +139,8 @@
onRemoveTile = onRemoveTile,
onSetTiles = onSetTiles,
onResize = iconTilesViewModel::resize,
+ onStopEditing = onStopEditing,
+ onReset = viewModel::showResetDialog,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegate.kt
new file mode 100644
index 0000000..03fc425
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegate.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.qs.panels.ui.dialog
+
+import android.util.Log
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import com.android.compose.PlatformButton
+import com.android.compose.PlatformOutlinedButton
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dialog.ui.composable.AlertDialogContent
+import com.android.systemui.qs.panels.domain.interactor.EditTilesResetInteractor
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.ComponentSystemUIDialog
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import com.android.systemui.statusbar.phone.create
+import com.android.systemui.util.Assert
+import javax.inject.Inject
+
+@SysUISingleton
+class QSResetDialogDelegate
+@Inject
+constructor(
+ private val sysuiDialogFactory: SystemUIDialogFactory,
+ private val resetInteractor: EditTilesResetInteractor,
+) : SystemUIDialog.Delegate {
+ private var currentDialog: ComponentSystemUIDialog? = null
+
+ override fun createDialog(): SystemUIDialog {
+ Assert.isMainThread()
+ if (currentDialog != null) {
+ Log.d(TAG, "Dialog is already open, dismissing it and creating a new one.")
+ currentDialog?.dismiss()
+ }
+
+ currentDialog =
+ sysuiDialogFactory
+ .create { ResetConfirmationDialog(it) }
+ .also {
+ it.lifecycle.addObserver(
+ object : DefaultLifecycleObserver {
+ override fun onStop(owner: LifecycleOwner) {
+ Assert.isMainThread()
+ currentDialog = null
+ }
+ }
+ )
+ }
+ return currentDialog!!
+ }
+
+ @Composable
+ private fun ResetConfirmationDialog(dialog: SystemUIDialog) {
+ AlertDialogContent(
+ title = { Text(text = stringResource(id = R.string.qs_edit_mode_reset_dialog_title)) },
+ content = {
+ Text(text = stringResource(id = R.string.qs_edit_mode_reset_dialog_content))
+ },
+ positiveButton = {
+ PlatformButton(
+ onClick = {
+ dialog.dismiss()
+ resetInteractor.reset()
+ }
+ ) {
+ Text(stringResource(id = android.R.string.ok))
+ }
+ },
+ neutralButton = {
+ PlatformOutlinedButton(onClick = { dialog.dismiss() }) {
+ Text(stringResource(id = android.R.string.cancel))
+ }
+ },
+ )
+ }
+
+ fun showDialog() {
+ if (currentDialog == null) {
+ createDialog()
+ }
+ currentDialog?.show()
+ }
+
+ companion object {
+ private const val TAG = "ResetDialogDelegate"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
new file mode 100644
index 0000000..9feaab8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.qs.panels.ui.viewmodel
+
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.qs.panels.domain.interactor.DynamicIconTilesInteractor
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** View model to resize QS tiles down to icons when removed from the current tiles. */
+class DynamicIconTilesViewModel
+@AssistedInject
+constructor(
+ interactorFactory: DynamicIconTilesInteractor.Factory,
+ iconTilesViewModel: IconTilesViewModel,
+) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() {
+ private val interactor = interactorFactory.create()
+
+ override suspend fun onActivated(): Nothing {
+ interactor.activate()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): DynamicIconTilesViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 4a8aa83e..7fe856b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -73,11 +73,7 @@
val gridLayout: StateFlow<GridLayout> =
gridLayoutTypeInteractor.layout
.map { gridLayoutMap[it] ?: defaultGridLayout }
- .stateIn(
- applicationScope,
- SharingStarted.WhileSubscribed(),
- defaultGridLayout,
- )
+ .stateIn(applicationScope, SharingStarted.WhileSubscribed(), defaultGridLayout)
/**
* Flow of view models for each tile that should be visible in edit mode (or empty flow when not
@@ -196,9 +192,4 @@
fun setTiles(tileSpecs: List<TileSpec>) {
currentTilesInteractor.setTiles(tileSpecs)
}
-
- /** Immediately resets the current tiles to the default list. */
- fun resetCurrentTilesToDefault() {
- throw NotImplementedError("This is not supported yet")
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
new file mode 100644
index 0000000..0d12067
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.qs.panels.ui.viewmodel
+
+import com.android.systemui.qs.panels.ui.dialog.QSResetDialogDelegate
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class InfiniteGridViewModel
+@AssistedInject
+constructor(
+ val dynamicIconTilesViewModelFactory: DynamicIconTilesViewModel.Factory,
+ val gridSizeViewModel: QSColumnsViewModel,
+ val squishinessViewModel: TileSquishinessViewModel,
+ private val resetDialogDelegate: QSResetDialogDelegate,
+) {
+
+ fun showResetDialog() {
+ resetDialogDelegate.showDialog()
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(): InfiniteGridViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index 24b80b8..d94e7cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -71,6 +71,9 @@
/** Prepend the default list of tiles to the current set of tiles */
suspend fun prependDefault(@UserIdInt userId: Int)
+ /** Reset the current set of tiles to the default list of tiles */
+ suspend fun resetToDefault(userId: Int)
+
companion object {
/** Position to indicate the end of the list */
const val POSITION_AT_END = -1
@@ -148,22 +151,24 @@
override suspend fun reconcileRestore(
restoreData: RestoreData,
- currentAutoAdded: Set<TileSpec>
+ currentAutoAdded: Set<TileSpec>,
) {
userTileRepositories
.get(restoreData.userId)
?.reconcileRestore(restoreData, currentAutoAdded)
}
- override suspend fun prependDefault(
- userId: Int,
- ) {
+ override suspend fun prependDefault(userId: Int) {
if (retailModeRepository.inRetailMode) {
return
}
userTileRepositories.get(userId)?.prependDefault()
}
+ override suspend fun resetToDefault(userId: Int) {
+ userTileRepositories.get(userId)?.resetToDefault()
+ }
+
companion object {
private const val DELIMITER = TilesSettingConverter.DELIMITER
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
index 1f9570a..b0ae1e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -119,14 +119,7 @@
.filter { it !is TileSpec.Invalid }
.joinToString(DELIMITER, transform = TileSpec::spec)
withContext(backgroundDispatcher) {
- secureSettings.putStringForUser(
- SETTING,
- toStore,
- null,
- false,
- forUser,
- true,
- )
+ secureSettings.putStringForUser(SETTING, toStore, null, false, forUser, true)
}
}
@@ -172,13 +165,17 @@
changeEvents.emit(PrependDefault(defaultTiles))
}
+ suspend fun resetToDefault() {
+ changeEvents.emit(ResetToDefault(defaultTiles))
+ }
+
sealed interface ChangeAction {
fun apply(currentTiles: List<TileSpec>): List<TileSpec>
}
private data class AddTile(
val tileSpec: TileSpec,
- val position: Int = TileSpecRepository.POSITION_AT_END
+ val position: Int = TileSpecRepository.POSITION_AT_END,
) : ChangeAction {
override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
val tilesList = currentTiles.toMutableList()
@@ -199,9 +196,7 @@
}
}
- private data class ChangeTiles(
- val newTiles: List<TileSpec>,
- ) : ChangeAction {
+ private data class ChangeTiles(val newTiles: List<TileSpec>) : ChangeAction {
override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
val new = newTiles.filter { it !is TileSpec.Invalid }
return if (new.isNotEmpty()) new else currentTiles
@@ -214,6 +209,12 @@
}
}
+ private data class ResetToDefault(val defaultTiles: List<TileSpec>) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ return defaultTiles
+ }
+ }
+
private data class RestoreTiles(
val restoreData: RestoreData,
val currentAutoAdded: Set<TileSpec>,
@@ -236,7 +237,7 @@
fun reconcileTiles(
currentTiles: List<TileSpec>,
currentAutoAdded: Set<TileSpec>,
- restoreData: RestoreData
+ restoreData: RestoreData,
): List<TileSpec> {
val toRestore = restoreData.restoredTiles.toMutableList()
val freshlyAutoAdded =
@@ -260,8 +261,6 @@
@AssistedFactory
interface Factory {
- fun create(
- userId: Int,
- ): UserTileSpecRepository
+ fun create(userId: Int): UserTileSpecRepository
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 4a96710..10097d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -116,6 +116,9 @@
*/
fun setTiles(specs: List<TileSpec>)
+ /** Requests that the list of tiles for the current user is changed to the default list. */
+ fun resetTiles()
+
fun createTileSync(spec: TileSpec): QSTile?
companion object {
@@ -222,7 +225,7 @@
.TILE_NOT_PRESENT_IN_NEW_USER
} else {
QSPipelineLogger.TileDestroyedReason.TILE_REMOVED
- }
+ },
)
(entry.value as TileOrNotInstalled.Tile).tile.destroy()
}
@@ -245,7 +248,7 @@
tileSpec,
specsToTiles.getValue(tileSpec),
userChanged,
- newUser
+ newUser,
) ?: createTile(tileSpec)
} else {
createTile(tileSpec)
@@ -268,7 +271,7 @@
_currentSpecsAndTiles.value = newResolvedTiles
logger.logTilesNotInstalled(
newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys,
- newUser
+ newUser,
)
if (newResolvedTiles.size < minTiles) {
// We ended up with not enough tiles (some may be not installed).
@@ -317,6 +320,10 @@
}
}
+ override fun resetTiles() {
+ scope.launch { tileSpecRepository.resetToDefault(currentUser.value) }
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("CurrentTileInteractorImpl:")
pw.println("User: ${userId.value}")
@@ -384,7 +391,7 @@
!qsTile.isAvailable -> {
logger.logTileDestroyed(
tileSpec,
- QSPipelineLogger.TileDestroyedReason.EXISTING_TILE_NOT_AVAILABLE
+ QSPipelineLogger.TileDestroyedReason.EXISTING_TILE_NOT_AVAILABLE,
)
qsTile.destroy()
null
@@ -409,7 +416,7 @@
qsTile.destroy()
logger.logTileDestroyed(
tileSpec,
- QSPipelineLogger.TileDestroyedReason.CUSTOM_TILE_USER_CHANGED
+ QSPipelineLogger.TileDestroyedReason.CUSTOM_TILE_USER_CHANGED,
)
null
}
@@ -428,7 +435,7 @@
private data class UserTilesAndComponents(
val userId: Int,
val tiles: List<TileSpec>,
- val installedComponents: Set<ComponentName>
+ val installedComponents: Set<ComponentName>,
)
private data class DataWithUserChange(
@@ -439,9 +446,4 @@
)
private fun DataWithUserChange(data: UserTilesAndComponents, userChange: Boolean) =
- DataWithUserChange(
- data.userId,
- data.tiles,
- data.installedComponents,
- userChange,
- )
+ DataWithUserChange(data.userId, data.tiles, data.installedComponents, userChange)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index 8d060e9..8a6df1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -67,6 +67,8 @@
onRemoveTile = {},
onSetTiles = onSetTiles,
onResize = { _, _ -> },
+ onStopEditing = {},
+ onReset = null,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
index ee1c0e9..d9c1d99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -66,6 +66,8 @@
onRemoveTile = {},
onSetTiles = {},
onResize = onResize,
+ onStopEditing = {},
+ onReset = null,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/DynamicIconTilesInteractorFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/DynamicIconTilesInteractorFactoryKosmos.kt
new file mode 100644
index 0000000..a5fe8cf
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/DynamicIconTilesInteractorFactoryKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
+
+val Kosmos.dynamicIconTilesInteractorFactory by
+ Kosmos.Fixture {
+ object : DynamicIconTilesInteractor.Factory {
+ override fun create(): DynamicIconTilesInteractor {
+ return DynamicIconTilesInteractor(iconTilesInteractor, currentTilesInteractor)
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
index b4317ad..b6b0a41 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
@@ -19,10 +19,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.qsColumnsViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.tileSquishinessViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.infiniteGridViewModelFactory
val Kosmos.infiniteGridLayout by
- Kosmos.Fixture {
- InfiniteGridLayout(iconTilesViewModel, qsColumnsViewModel, tileSquishinessViewModel)
- }
+ Kosmos.Fixture { InfiniteGridLayout(iconTilesViewModel, infiniteGridViewModelFactory) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/SizedTilesResetInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/SizedTilesResetInteractorKosmos.kt
new file mode 100644
index 0000000..70bf9bb
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/SizedTilesResetInteractorKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.internal.logging.uiEventLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
+
+val Kosmos.sizedTilesResetInteractor by
+ Kosmos.Fixture {
+ SizedTilesResetInteractor(currentTilesInteractor, iconTilesInteractor, uiEventLogger)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegateKosmos.kt
new file mode 100644
index 0000000..c58d55e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/dialog/QSResetDialogDelegateKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.qs.panels.ui.dialog
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.domain.interactor.sizedTilesResetInteractor
+import com.android.systemui.statusbar.phone.systemUIDialogFactory
+
+val Kosmos.qsResetDialogDelegateKosmos by
+ Kosmos.Fixture { QSResetDialogDelegate(systemUIDialogFactory, sizedTilesResetInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModelKosmosFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModelKosmosFactory.kt
new file mode 100644
index 0000000..d185287
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModelKosmosFactory.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.qs.panels.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.domain.interactor.dynamicIconTilesInteractorFactory
+
+val Kosmos.dynamicIconTilesViewModelFactory by
+ Kosmos.Fixture {
+ object : DynamicIconTilesViewModel.Factory {
+ override fun create(): DynamicIconTilesViewModel {
+ return DynamicIconTilesViewModel(
+ dynamicIconTilesInteractorFactory,
+ iconTilesViewModel,
+ )
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt
new file mode 100644
index 0000000..7613ea31
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/InfiniteGridViewModelKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.qs.panels.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.ui.dialog.qsResetDialogDelegateKosmos
+
+val Kosmos.infiniteGridViewModelFactory by
+ Kosmos.Fixture {
+ object : InfiniteGridViewModel.Factory {
+ override fun create(): InfiniteGridViewModel {
+ return InfiniteGridViewModel(
+ dynamicIconTilesViewModelFactory,
+ qsColumnsViewModel,
+ tileSquishinessViewModel,
+ qsResetDialogDelegateKosmos,
+ )
+ }
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
index a9cce69..1c69eab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
@@ -63,7 +63,7 @@
override suspend fun reconcileRestore(
restoreData: RestoreData,
- currentAutoAdded: Set<TileSpec>
+ currentAutoAdded: Set<TileSpec>,
) {
with(getFlow(restoreData.userId)) {
value = UserTileSpecRepository.reconcileTiles(value, currentAutoAdded, restoreData)
@@ -73,4 +73,8 @@
override suspend fun prependDefault(userId: Int) {
with(getFlow(userId)) { value = defaultTilesRepository.defaultTiles + value }
}
+
+ override suspend fun resetToDefault(userId: Int) {
+ with(getFlow(userId)) { value = defaultTilesRepository.defaultTiles }
+ }
}