diff options
| author | 2024-09-03 14:52:30 -0400 | |
|---|---|---|
| committer | 2024-09-09 14:02:01 -0400 | |
| commit | 29e397412d87de8ed7ed724a9ed9407f600a58fa (patch) | |
| tree | d591ed921cc59ceb9c166ebeac191592c056658a | |
| parent | 366dbf89935270dc68b2a98346c875191db01ce7 (diff) | |
Add categories to tiles for edit mode
This CL adds categories to the QSTileConfig. These are used for grouping
and sorting the tiles that can be added into groups, making them easier
to parse.
Test: manual
Test: atest GroupAndSortCategoryAndNameTest
Bug: 364329418
Flag: com.android.systemui.qs_ui_refactor_compose_fragment
Change-Id: I2ef086502d68f4d2ea484411e2d1d6101d02075a
33 files changed, 401 insertions, 97 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt index e1f73e304b9e..4e8121f14e4b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/TextExt.kt @@ -17,9 +17,12 @@ package com.android.systemui.common.ui.compose +import android.content.Context import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString import com.android.systemui.common.shared.model.Text +import com.android.systemui.common.shared.model.Text.Companion.loadText /** Returns the loaded [String] or `null` if there isn't one. */ @Composable @@ -29,3 +32,7 @@ fun Text.load(): String? { is Text.Resource -> stringResource(res) } } + +fun Text.toAnnotatedString(context: Context): AnnotatedString? { + return loadText(context)?.let { AnnotatedString(it) } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt index 1e5599bfe1d5..93ba26517e37 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepositoryTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.qs.panels.data.repository import android.content.ComponentName import android.content.packageManager import android.content.pm.PackageManager -import android.content.pm.ServiceInfo import android.content.pm.UserInfo import android.graphics.drawable.TestStubDrawable import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -35,6 +34,7 @@ import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesCompon import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.settings.FakeUserTracker import com.android.systemui.settings.fakeUserTracker import com.android.systemui.testKosmos @@ -100,6 +100,7 @@ class IconAndNameCustomRepositoryTest : SysuiTestCase() { Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)), Text.Loaded(tileService1), Text.Loaded(appName1), + TileCategory.PROVIDED_BY_APP, ) val expectedData2 = EditTileData( @@ -107,6 +108,7 @@ class IconAndNameCustomRepositoryTest : SysuiTestCase() { Icon.Loaded(drawable2, ContentDescription.Loaded(tileService2)), Text.Loaded(tileService2), Text.Loaded(appName2), + TileCategory.PROVIDED_BY_APP, ) assertThat(editTileDataList).containsExactly(expectedData1, expectedData2) @@ -144,6 +146,7 @@ class IconAndNameCustomRepositoryTest : SysuiTestCase() { Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)), Text.Loaded(tileService1), Text.Loaded(appName1), + TileCategory.PROVIDED_BY_APP, ) val editTileDataList = underTest.getCustomTileData() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt index deefbf585ba9..053a59aa533a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.qs.panels.shared.model.EditTileData import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesComponentRepository import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig @@ -132,6 +133,7 @@ class EditTilesListInteractorTest : SysuiTestCase() { icon = Icon.Loaded(icon, ContentDescription.Loaded(tileName)), label = Text.Loaded(tileName), appName = Text.Loaded(appName), + category = TileCategory.PROVIDED_BY_APP, ) assertThat(editTiles.customTiles).hasSize(1) @@ -181,7 +183,8 @@ class EditTilesListInteractorTest : SysuiTestCase() { tileSpec = this, icon = Icon.Resource(android.R.drawable.star_on, ContentDescription.Loaded(spec)), label = Text.Loaded(spec), - appName = null + appName = null, + category = TileCategory.UNKNOWN, ) } @@ -192,6 +195,7 @@ class EditTilesListInteractorTest : SysuiTestCase() { Icon.Resource(uiConfig.iconRes, ContentDescription.Resource(uiConfig.labelRes)), label = Text.Resource(uiConfig.labelRes), appName = null, + category = category, ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt index 7f01fad1ce55..484a8ff973c1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt @@ -16,11 +16,11 @@ package com.android.systemui.qs.panels.ui.compose +import androidx.compose.ui.text.AnnotatedString import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon -import com.android.systemui.common.shared.model.Text import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl import com.android.systemui.qs.panels.ui.model.GridCell @@ -28,6 +28,7 @@ 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.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -141,10 +142,11 @@ class EditTileListStateTest : SysuiTestCase() { EditTileViewModel( tileSpec = TileSpec.create(tileSpec), icon = Icon.Resource(0, null), - label = Text.Loaded("unused"), + label = AnnotatedString("unused"), appName = null, isCurrent = true, availableEditActions = emptySet(), + category = TileCategory.UNKNOWN, ), width, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt index 601779f8fb02..583db722a759 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text +import com.android.systemui.common.ui.compose.toAnnotatedString import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope @@ -42,6 +43,7 @@ import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesReposito import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.qsTileFactory +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig @@ -190,7 +192,7 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { .forEach { val data = getEditTileData(it.tileSpec) - assertThat(it.label).isEqualTo(data.label) + assertThat(it.label).isEqualTo(data.label.toAnnotatedString(context)) assertThat(it.icon).isEqualTo(data.icon) assertThat(it.appName).isNull() } @@ -224,15 +226,19 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { // service1 val model1 = tiles!!.first { it.tileSpec == TileSpec.create(component1) } - assertThat(model1.label).isEqualTo(Text.Loaded(tileService1)) - assertThat(model1.appName).isEqualTo(Text.Loaded(appName1)) + assertThat(model1.label) + .isEqualTo(Text.Loaded(tileService1).toAnnotatedString(context)) + assertThat(model1.appName) + .isEqualTo(Text.Loaded(appName1).toAnnotatedString(context)) assertThat(model1.icon) .isEqualTo(Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1))) // service2 val model2 = tiles!!.first { it.tileSpec == TileSpec.create(component2) } - assertThat(model2.label).isEqualTo(Text.Loaded(tileService2)) - assertThat(model2.appName).isEqualTo(Text.Loaded(appName2)) + assertThat(model2.label) + .isEqualTo(Text.Loaded(tileService2).toAnnotatedString(context)) + assertThat(model2.appName) + .isEqualTo(Text.Loaded(appName2).toAnnotatedString(context)) assertThat(model2.icon) .isEqualTo(Icon.Loaded(drawable2, ContentDescription.Loaded(tileService2))) } @@ -559,7 +565,8 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { tileSpec = this, icon = Icon.Resource(R.drawable.star_on, ContentDescription.Loaded(spec)), label = Text.Loaded(spec), - appName = null + appName = null, + category = TileCategory.UNKNOWN, ) } @@ -570,6 +577,7 @@ class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { Icon.Resource(uiConfig.iconRes, ContentDescription.Resource(uiConfig.labelRes)), label = Text.Resource(uiConfig.labelRes), appName = null, + category = category, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/model/GroupAndSortCategoryAndNameTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/model/GroupAndSortCategoryAndNameTest.kt new file mode 100644 index 000000000000..7f90e3b6f5e0 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/model/GroupAndSortCategoryAndNameTest.kt @@ -0,0 +1,87 @@ +/* + * 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.shared.model + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class GroupAndSortCategoryAndNameTest : SysuiTestCase() { + + private val elements = + listOf( + CategoryAndName(TileCategory.DISPLAY, "B"), + CategoryAndName(TileCategory.PRIVACY, "A"), + CategoryAndName(TileCategory.DISPLAY, "C"), + CategoryAndName(TileCategory.UTILITIES, "B"), + CategoryAndName(TileCategory.CONNECTIVITY, "A"), + CategoryAndName(TileCategory.PROVIDED_BY_APP, "B"), + CategoryAndName(TileCategory.CONNECTIVITY, "C"), + CategoryAndName(TileCategory.ACCESSIBILITY, "A") + ) + + @Test + fun allElementsInResult() { + val grouped = groupAndSort(elements) + val allValues = grouped.values.reduce { acc, el -> acc + el } + assertThat(allValues).containsExactlyElementsIn(elements) + } + + @Test + fun groupedByCategory() { + val grouped = groupAndSort(elements) + grouped.forEach { tileCategory, categoryAndNames -> + categoryAndNames.forEach { element -> + assertThat(element.category).isEqualTo(tileCategory) + } + } + } + + @Test + fun sortedAlphabeticallyInEachCategory() { + val grouped = groupAndSort(elements) + grouped.values.forEach { elements -> + assertThat(elements.map(CategoryAndName::name)).isInOrder() + } + } + + @Test + fun categoriesSortedInNaturalOrder() { + val grouped = groupAndSort(elements) + assertThat(grouped.keys).isInOrder() + } + + @Test + fun missingCategoriesAreNotInResult() { + val grouped = groupAndSort(elements.filterNot { it.category == TileCategory.CONNECTIVITY }) + assertThat(grouped.keys).doesNotContain(TileCategory.CONNECTIVITY) + } + + companion object { + private fun CategoryAndName(category: TileCategory, name: String): CategoryAndName { + return object : CategoryAndName { + override val category = category + override val name = name + } + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt index 244422943309..fa6d8bf4a317 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt @@ -24,6 +24,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testCase import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.qsEventLogger +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig @@ -43,7 +44,8 @@ class IssueRecordingMapperTest : SysuiTestCase() { QSTileConfig( TileSpec.create(RecordIssueModule.TILE_SPEC), uiConfig, - kosmos.qsEventLogger.getNewInstanceId() + kosmos.qsEventLogger.getNewInstanceId(), + TileCategory.UTILITIES, ) private val resources = kosmos.mainResources private val theme = resources.newTheme() diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e6cc6cf766c6..5ffa07d48b75 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3787,4 +3787,33 @@ Action + ESC for this.</string> <!-- Toast message for notifying users to use regular brightness bar to lower the brightness. [CHAR LIMIT=NONE] --> <string name="accessibility_deprecate_extra_dim_dialog_toast"> Extra dim shortcut removed. To lower your brightness, use the regular brightness bar.</string> + + <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to connectivity, e.g. Internet. [CHAR LIMIT=NONE] --> + <string name="qs_edit_mode_category_connectivity"> + Connectivity + </string> + <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to accessibility functions, e.g. Hearing devices. [CHAR LIMIT=NONE] --> + <string name="qs_edit_mode_category_accessibility"> + Accessibility + </string> + <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to general utilities, e.g. Flashlight. [CHAR LIMIT=NONE] --> + <string name="qs_edit_mode_category_utilities"> + Utilities + </string> + <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to privacy, e.g. Mic access. [CHAR LIMIT=NONE] --> + <string name="qs_edit_mode_category_privacy"> + Privacy + </string> + <!-- Label for category in QS Edit mode list of tiles, for tiles that are provided by an app, e.g. Calculator. [CHAR LIMIT=NONE] --> + <string name="qs_edit_mode_category_providedByApps"> + Provided by apps + </string> + <!-- Label for category in QS Edit mode list of tiles, for tiles that are related to the display, e.g. Dark theme. [CHAR LIMIT=NONE] --> + <string name="qs_edit_mode_category_display"> + Display + </string> + <!-- Label for category in QS Edit mode list of tiles, for tiles with an unknown category. [CHAR LIMIT=NONE] --> + <string name="qs_edit_mode_category_unknown"> + Unknown + </string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt index bb80396c70fb..cd9efaf6e6bb 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt @@ -19,6 +19,7 @@ package com.android.systemui.accessibility.qs import com.android.systemui.Flags import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.ColorCorrectionTile import com.android.systemui.qs.tiles.ColorInversionTile @@ -179,6 +180,7 @@ interface QSAccessibilityModule { labelRes = R.string.quick_settings_color_correction_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.ACCESSIBILITY, ) /** Inject ColorCorrectionTile into tileViewModelMap in QSModule */ @@ -210,6 +212,7 @@ interface QSAccessibilityModule { labelRes = R.string.quick_settings_inversion_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.ACCESSIBILITY, ) /** Inject ColorInversionTile into tileViewModelMap in QSModule */ @@ -241,6 +244,7 @@ interface QSAccessibilityModule { labelRes = R.string.quick_settings_font_scaling_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.DISPLAY, ) /** Inject FontScaling Tile into tileViewModelMap in QSModule */ @@ -272,6 +276,7 @@ interface QSAccessibilityModule { labelRes = com.android.internal.R.string.reduce_bright_colors_feature_name, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.DISPLAY, ) @Provides @@ -286,6 +291,7 @@ interface QSAccessibilityModule { labelRes = R.string.quick_settings_hearing_devices_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.ACCESSIBILITY, ) /** @@ -322,6 +328,7 @@ interface QSAccessibilityModule { labelRes = R.string.quick_settings_onehanded_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.ACCESSIBILITY, ) /** Inject One Handed Mode Tile into tileViewModelMap in QSModule. */ @@ -355,6 +362,7 @@ interface QSAccessibilityModule { labelRes = R.string.quick_settings_night_display_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.DISPLAY, ) /** diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt index 8a9a322ff100..831bc1da5a79 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt @@ -2,6 +2,7 @@ package com.android.systemui.battery import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.BatterySaverTile import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor @@ -33,7 +34,7 @@ interface BatterySaverModule { @IntoMap @StringKey(BATTERY_SAVER_TILE_SPEC) fun provideBatterySaverAvailabilityInteractor( - impl: BatterySaverTileDataInteractor + impl: BatterySaverTileDataInteractor ): QSTileAvailabilityInteractor companion object { @@ -51,6 +52,7 @@ interface BatterySaverModule { labelRes = R.string.battery_detail_switch_title, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.UTILITIES, ) /** Inject BatterySaverTile into tileViewModelMap in QSModule */ diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt index db7ffc1bef79..037b6facc50d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt @@ -48,6 +48,7 @@ import com.android.systemui.controls.ui.ControlsUiControllerImpl import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.DeviceControlsTile import com.android.systemui.qs.tiles.viewmodel.QSTileConfig @@ -86,15 +87,16 @@ abstract class ControlsModule { @IntoMap @StringKey(DEVICE_CONTROLS_SPEC) fun provideDeviceControlsTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = - QSTileConfig( - tileSpec = TileSpec.create(DEVICE_CONTROLS_SPEC), - uiConfig = - QSTileUIConfig.Resource( - iconRes = com.android.systemui.res.R.drawable.controls_icon, - labelRes = com.android.systemui.res.R.string.quick_controls_title - ), - instanceId = uiEventLogger.getNewInstanceId(), - ) + QSTileConfig( + tileSpec = TileSpec.create(DEVICE_CONTROLS_SPEC), + uiConfig = + QSTileUIConfig.Resource( + iconRes = com.android.systemui.res.R.drawable.controls_icon, + labelRes = com.android.systemui.res.R.string.quick_controls_title + ), + instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.UTILITIES, + ) } @Binds @@ -115,12 +117,12 @@ abstract class ControlsModule { @Binds abstract fun provideSettingsManager( - manager: ControlsSettingsRepositoryImpl + manager: ControlsSettingsRepositoryImpl ): ControlsSettingsRepository @Binds abstract fun provideDialogManager( - manager: ControlsSettingsDialogManagerImpl + manager: ControlsSettingsDialogManagerImpl ): ControlsSettingsDialogManager @Binds @@ -141,8 +143,7 @@ abstract class ControlsModule { repository: SelectedComponentRepositoryImpl ): SelectedComponentRepository - @BindsOptionalOf - abstract fun optionalPersistenceWrapper(): ControlsFavoritePersistenceWrapper + @BindsOptionalOf abstract fun optionalPersistenceWrapper(): ControlsFavoritePersistenceWrapper @BindsOptionalOf abstract fun provideControlsTileResourceConfiguration(): ControlsTileResourceConfiguration @@ -157,23 +158,17 @@ abstract class ControlsModule { @Binds @IntoMap @ClassKey(ControlsFavoritingActivity::class) - abstract fun provideControlsFavoritingActivity( - activity: ControlsFavoritingActivity - ): Activity + abstract fun provideControlsFavoritingActivity(activity: ControlsFavoritingActivity): Activity @Binds @IntoMap @ClassKey(ControlsEditingActivity::class) - abstract fun provideControlsEditingActivity( - activity: ControlsEditingActivity - ): Activity + abstract fun provideControlsEditingActivity(activity: ControlsEditingActivity): Activity @Binds @IntoMap @ClassKey(ControlsRequestDialog::class) - abstract fun provideControlsRequestDialog( - activity: ControlsRequestDialog - ): Activity + abstract fun provideControlsRequestDialog(activity: ControlsRequestDialog): Activity @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java index f6ac7a579140..a45ad157837b 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java @@ -38,6 +38,7 @@ import com.android.systemui.dreams.homecontrols.DreamServiceDelegateImpl; import com.android.systemui.dreams.homecontrols.HomeControlsDreamService; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.pipeline.shared.TileSpec; +import com.android.systemui.qs.shared.model.TileCategory; import com.android.systemui.qs.tiles.viewmodel.QSTileConfig; import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy; import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig; @@ -196,6 +197,7 @@ public interface DreamModule { R.drawable.ic_qs_screen_saver, R.string.quick_settings_screensaver_label), uiEventLogger.getNewInstanceId(), + TileCategory.UTILITIES, tileSpec.getSpec(), QSTilePolicy.NoRestrictions.INSTANCE ); diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt index 3a8fe7198ebd..ef1f8341cb15 100644 --- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt @@ -19,6 +19,7 @@ package com.android.systemui.qrcodescanner.dagger import com.android.systemui.Flags import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.QRCodeScannerTile import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor @@ -51,7 +52,7 @@ interface QRCodeScannerModule { @IntoMap @StringKey(QR_CODE_SCANNER_TILE_SPEC) fun provideQrCodeScannerAvailabilityInteractor( - impl: QRCodeScannerTileDataInteractor + impl: QRCodeScannerTileDataInteractor ): QSTileAvailabilityInteractor companion object { @@ -69,6 +70,7 @@ interface QRCodeScannerModule { labelRes = R.string.qr_code_scanner_title, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.UTILITIES, ) /** Inject QR Code Scanner Tile into tileViewModelMap in QSModule. */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt index 28c1fbf2007d..2f054b0a82c0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconAndNameCustomRepository.kt @@ -24,10 +24,10 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.panels.shared.model.EditTileData import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.settings.UserTracker import javax.inject.Inject import kotlin.coroutines.CoroutineContext -import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext @SysUISingleton @@ -60,6 +60,7 @@ constructor( Icon.Loaded(icon, ContentDescription.Loaded(label.toString())), Text.Loaded(label.toString()), Text.Loaded(appName.toString()), + TileCategory.PROVIDED_BY_APP, ) } else { null diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt index 3b29422ccfc3..a2cee3b68d49 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt @@ -24,6 +24,7 @@ import com.android.systemui.qs.panels.data.repository.IconAndNameCustomRepositor import com.android.systemui.qs.panels.data.repository.StockTilesRepository import com.android.systemui.qs.panels.domain.model.EditTilesModel import com.android.systemui.qs.panels.shared.model.EditTileData +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider import javax.inject.Inject @@ -53,6 +54,7 @@ constructor( ), Text.Resource(config.uiConfig.labelRes), null, + category = config.category, ) } else { EditTileData( @@ -62,7 +64,8 @@ constructor( ContentDescription.Loaded(it.spec) ), Text.Loaded(it.spec), - null + null, + category = TileCategory.UNKNOWN, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt index 8b70bb9f9e23..b153ef7e8a62 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/EditTileData.kt @@ -19,12 +19,14 @@ package com.android.systemui.qs.panels.shared.model import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory data class EditTileData( val tileSpec: TileSpec, val icon: Icon, val label: Text, val appName: Text?, + val category: TileCategory, ) { init { check( diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt index 79c2eb90af20..6d96f5c13238 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt @@ -34,6 +34,7 @@ import androidx.compose.animation.graphics.vector.AnimatedImageVector import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.LocalOverscrollConfiguration +import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable @@ -96,6 +97,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.util.fastMap import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.Expandable import com.android.compose.modifiers.background @@ -116,6 +119,7 @@ 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 import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.groupAndSort import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.res.R import java.util.function.Supplier @@ -473,31 +477,39 @@ private fun AvailableTileGrid( onClick: (TileSpec) -> Unit, dragAndDropState: DragAndDropState, ) { - // Available tiles aren't visible during drag and drop, so the row isn't needed - val (otherTilesStock, otherTilesCustom) = - tiles.map { TileGridCell(it, 0) }.partition { it.tile.appName == null } val availableTileHeight = tileHeight(true) val availableGridHeight = gridHeight(tiles.size, availableTileHeight, columns, tilePadding) + // Available tiles aren't visible during drag and drop, so the row isn't needed + val groupedTiles = + remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) { + groupAndSort(tiles.fastMap { TileGridCell(it, 0) }) + } + val labelColors = TileDefaults.inactiveTileColors() // Available tiles TileLazyGrid( modifier = Modifier.height(availableGridHeight).testTag(AVAILABLE_TILES_GRID_TEST_TAG), columns = GridCells.Fixed(columns) ) { - editTiles( - otherTilesStock, - ClickAction.ADD, - onClick, - dragAndDropState = dragAndDropState, - showLabels = true, - ) - editTiles( - otherTilesCustom, - ClickAction.ADD, - onClick, - dragAndDropState = dragAndDropState, - showLabels = true, - ) + groupedTiles.forEach { category, tiles -> + stickyHeader { + Text( + text = category.label.load() ?: "", + fontSize = 20.sp, + color = labelColors.label, + modifier = + Modifier.background(Color.Black) + .padding(start = 16.dp, bottom = 8.dp, top = 8.dp) + ) + } + editTiles( + tiles, + ClickAction.ADD, + onClick, + dragAndDropState = dragAndDropState, + showLabels = true, + ) + } } } @@ -619,7 +631,7 @@ fun EditTile( showLabels: Boolean, modifier: Modifier = Modifier, ) { - val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec + val label = tileViewModel.label.text val colors = TileDefaults.inactiveTileColors() TileContainer( @@ -639,7 +651,7 @@ fun EditTile( } else { LargeTileContent( label = label, - secondaryLabel = tileViewModel.appName?.load(), + secondaryLabel = tileViewModel.appName?.text, icon = tileViewModel.icon, colors = colors, iconShape = RoundedCornerShape(TileDefaults.InactiveCornerRadius), diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt index 8ca8de762772..08ee856a0ec6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt @@ -21,6 +21,7 @@ import androidx.compose.runtime.Immutable import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.splitInRowsSequence import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel +import com.android.systemui.qs.shared.model.CategoryAndName /** Represents an item from a grid associated with a row and a span */ interface GridCell { @@ -38,7 +39,7 @@ data class TileGridCell( override val row: Int, override val width: Int, override val span: GridItemSpan = GridItemSpan(width) -) : GridCell, SizedTile<EditTileViewModel> { +) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile { val key: String = "${tile.tileSpec.spec}-$row" constructor( 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 42715be6f6c0..4a8aa83e7d4e 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 @@ -16,6 +16,9 @@ package com.android.systemui.qs.panels.ui.viewmodel +import android.content.Context +import androidx.compose.ui.util.fastMap +import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.qs.panels.domain.interactor.EditTilesListInteractor @@ -27,6 +30,7 @@ import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.util.kotlin.emitOnStart import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.CoroutineScope @@ -35,6 +39,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map @@ -49,6 +54,8 @@ constructor( private val currentTilesInteractor: CurrentTilesInteractor, private val tilesAvailabilityInteractor: TilesAvailabilityInteractor, private val minTilesInteractor: MinimumTilesInteractor, + private val configurationInteractor: ConfigurationInteractor, + @Application private val applicationContext: Context, @Named("Default") private val defaultGridLayout: GridLayout, @Application private val applicationScope: CoroutineScope, gridLayoutTypeInteractor: GridLayoutTypeInteractor, @@ -99,38 +106,45 @@ constructor( .map { it.tileSpec } .minus(currentTilesInteractor.currentTilesSpecs.toSet()) ) - currentTilesInteractor.currentTiles.map { tiles -> - val currentSpecs = tiles.map { it.spec } - val canRemoveTiles = currentSpecs.size > minimumTiles - val allTiles = editTilesData.stockTiles + editTilesData.customTiles - val allTilesMap = allTiles.associate { it.tileSpec to it } - val currentTiles = currentSpecs.map { allTilesMap.get(it) }.filterNotNull() - val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs } - - (currentTiles + nonCurrentTiles) - .filterNot { it.tileSpec in unavailable } - .map { - val current = it.tileSpec in currentSpecs - val availableActions = buildSet { - if (current) { - add(AvailableEditActions.MOVE) - if (canRemoveTiles) { - add(AvailableEditActions.REMOVE) + currentTilesInteractor.currentTiles + .map { tiles -> + val currentSpecs = tiles.map { it.spec } + val canRemoveTiles = currentSpecs.size > minimumTiles + val allTiles = editTilesData.stockTiles + editTilesData.customTiles + val allTilesMap = allTiles.associate { it.tileSpec to it } + val currentTiles = currentSpecs.map { allTilesMap.get(it) }.filterNotNull() + val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs } + + (currentTiles + nonCurrentTiles) + .filterNot { it.tileSpec in unavailable } + .map { + val current = it.tileSpec in currentSpecs + val availableActions = buildSet { + if (current) { + add(AvailableEditActions.MOVE) + if (canRemoveTiles) { + add(AvailableEditActions.REMOVE) + } + } else { + add(AvailableEditActions.ADD) } - } else { - add(AvailableEditActions.ADD) } + UnloadedEditTileViewModel( + it.tileSpec, + it.icon, + it.label, + it.appName, + current, + availableActions, + it.category, + ) } - EditTileViewModel( - it.tileSpec, - it.icon, - it.label, - it.appName, - current, - availableActions - ) - } - } + } + .combine(configurationInteractor.onAnyConfigurationChange.emitOnStart()) { + tiles, + _ -> + tiles.fastMap { it.load(applicationContext) } + } } else { emptyFlow() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt index a4c86381b785..ee12736f6db4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt @@ -16,9 +16,15 @@ package com.android.systemui.qs.panels.ui.viewmodel +import android.content.Context +import androidx.compose.runtime.Immutable +import androidx.compose.ui.text.AnnotatedString import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text +import com.android.systemui.common.ui.compose.toAnnotatedString import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.CategoryAndName +import com.android.systemui.qs.shared.model.TileCategory /** * View model for each tile that is available to be added/removed/moved in Edit mode. @@ -26,14 +32,41 @@ import com.android.systemui.qs.pipeline.shared.TileSpec * [isCurrent] indicates whether this tile is part of the current set of tiles that the user sees in * Quick Settings. */ -data class EditTileViewModel( +data class UnloadedEditTileViewModel( val tileSpec: TileSpec, val icon: Icon, val label: Text, val appName: Text?, val isCurrent: Boolean, val availableEditActions: Set<AvailableEditActions>, -) + val category: TileCategory, +) { + fun load(context: Context): EditTileViewModel { + return EditTileViewModel( + tileSpec, + icon, + label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec), + appName?.toAnnotatedString(context), + isCurrent, + availableEditActions, + category, + ) + } +} + +@Immutable +data class EditTileViewModel( + val tileSpec: TileSpec, + val icon: Icon, + val label: AnnotatedString, + val appName: AnnotatedString?, + val isCurrent: Boolean, + val availableEditActions: Set<AvailableEditActions>, + override val category: TileCategory, +) : CategoryAndName { + override val name + get() = label.text +} enum class AvailableEditActions { ADD, diff --git a/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt b/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt new file mode 100644 index 000000000000..59cb7d3d5345 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/shared/model/TileCategory.kt @@ -0,0 +1,46 @@ +/* + * 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.shared.model + +import com.android.systemui.common.shared.model.Text +import com.android.systemui.res.R + +/** Categories for tiles. This can be used to sort tiles in edit mode. */ +enum class TileCategory(val label: Text) { + CONNECTIVITY(Text.Resource(R.string.qs_edit_mode_category_connectivity)), + UTILITIES(Text.Resource(R.string.qs_edit_mode_category_utilities)), + DISPLAY(Text.Resource(R.string.qs_edit_mode_category_display)), + PRIVACY(Text.Resource(R.string.qs_edit_mode_category_privacy)), + ACCESSIBILITY(Text.Resource(R.string.qs_edit_mode_category_accessibility)), + PROVIDED_BY_APP(Text.Resource(R.string.qs_edit_mode_category_providedByApps)), + UNKNOWN(Text.Resource(R.string.qs_edit_mode_category_unknown)), +} + +interface CategoryAndName { + val category: TileCategory + val name: String +} + +/** + * Groups the elements of the list by [CategoryAndName.category] (with the keys sorted in the + * natural order of [TileCategory]), and sorts the elements of each group based on the + * [CategoryAndName.name]. + */ +fun <T : CategoryAndName> groupAndSort(list: List<T>): Map<TileCategory, List<T>> { + val groupedByCategory = list.groupBy { it.category }.toSortedMap() + return groupedByCategory.mapValues { it.value.sortedBy { it.name } } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt index cdcefdb50b0f..3a9cb170040c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt @@ -21,6 +21,7 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import com.android.internal.logging.InstanceId import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory data class QSTileConfig @JvmOverloads @@ -28,6 +29,7 @@ constructor( val tileSpec: TileSpec, val uiConfig: QSTileUIConfig, val instanceId: InstanceId, + val category: TileCategory, val metricsSpec: String = tileSpec.spec, val policy: QSTilePolicy = QSTilePolicy.NoRestrictions, val autoRemoveOnUnavailable: Boolean = true, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt index 0609e797d53b..4dbddd91092a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt @@ -20,6 +20,7 @@ import com.android.internal.util.Preconditions import com.android.systemui.dagger.SysUISingleton import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import javax.inject.Inject interface QSTileConfigProvider { @@ -73,6 +74,7 @@ constructor( spec, QSTileUIConfig.Empty, qsEventLogger.getNewInstanceId(), + category = TileCategory.PROVIDED_BY_APP, ) is TileSpec.Invalid -> throw IllegalArgumentException("TileSpec.Invalid doesn't support configs") diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt index 907b92ce4c9b..c092c2f86799 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt @@ -19,6 +19,7 @@ package com.android.systemui.recordissue import com.android.systemui.Flags import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.RecordIssueTile import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory @@ -61,6 +62,7 @@ interface RecordIssueModule { labelRes = R.string.qs_record_issue_label ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.UTILITIES, ) /** Inject FlashlightTile into tileViewModelMap in QSModule */ diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt index 0589e6c63931..c9712fcdaa27 100644 --- a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt +++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt @@ -19,6 +19,7 @@ package com.android.systemui.rotationlock import com.android.systemui.camera.CameraRotationModule import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileDataInteractor @@ -42,8 +43,9 @@ interface RotationLockNewModule { @IntoMap @StringKey(ROTATION_TILE_SPEC) fun provideRotationAvailabilityInteractor( - impl: RotationLockTileDataInteractor + impl: RotationLockTileDataInteractor ): QSTileAvailabilityInteractor + companion object { private const val ROTATION_TILE_SPEC = "rotation" @@ -60,6 +62,7 @@ interface RotationLockNewModule { labelRes = R.string.quick_settings_rotation_unlocked_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.DISPLAY, ) /** Inject Rotation tile into tileViewModelMap in QSModule */ diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt index a830e1bbfe72..9a9c576c5af6 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt @@ -21,6 +21,7 @@ import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.ScreenRecordTile import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor @@ -74,6 +75,7 @@ interface ScreenRecordModule { labelRes = R.string.quick_settings_screen_record_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.DISPLAY, ) /** Inject ScreenRecord Tile into tileViewModelMap in QSModule */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt index 400f8af99e07..dac01028ef64 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt @@ -21,6 +21,7 @@ import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.AirplaneModeTile import com.android.systemui.qs.tiles.BluetoothTile @@ -95,21 +96,21 @@ interface ConnectivityModule { @IntoMap @StringKey(AIRPLANE_MODE_TILE_SPEC) fun provideAirplaneModeAvailabilityInteractor( - impl: AirplaneModeTileDataInteractor + impl: AirplaneModeTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(DATA_SAVER_TILE_SPEC) fun provideDataSaverAvailabilityInteractor( - impl: DataSaverTileDataInteractor + impl: DataSaverTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(INTERNET_TILE_SPEC) fun provideInternetAvailabilityInteractor( - impl: InternetTileDataInteractor + impl: InternetTileDataInteractor ): QSTileAvailabilityInteractor companion object { @@ -149,6 +150,7 @@ interface ConnectivityModule { ), instanceId = uiEventLogger.getNewInstanceId(), policy = QSTilePolicy.Restricted(listOf(UserManager.DISALLOW_AIRPLANE_MODE)), + category = TileCategory.CONNECTIVITY, ) /** Inject AirplaneModeTile into tileViewModelMap in QSModule */ @@ -180,6 +182,7 @@ interface ConnectivityModule { labelRes = R.string.data_saver, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.CONNECTIVITY, ) /** Inject DataSaverTile into tileViewModelMap in QSModule */ @@ -211,6 +214,7 @@ interface ConnectivityModule { labelRes = R.string.quick_settings_internet_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.CONNECTIVITY, ) /** Inject InternetTile into tileViewModelMap in QSModule */ @@ -242,6 +246,7 @@ interface ConnectivityModule { labelRes = R.string.quick_settings_hotspot_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.CONNECTIVITY, ) @Provides @@ -256,6 +261,7 @@ interface ConnectivityModule { labelRes = R.string.quick_settings_cast_title, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.CONNECTIVITY, ) @Provides @@ -270,6 +276,7 @@ interface ConnectivityModule { labelRes = R.string.quick_settings_bluetooth_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.CONNECTIVITY, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt index 21ec14fc7f03..5ff77e7ab24f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt @@ -23,6 +23,7 @@ import android.os.UserManager.DISALLOW_SHARE_LOCATION import com.android.systemui.Flags import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.AlarmTile import com.android.systemui.qs.tiles.CameraToggleTile @@ -157,6 +158,7 @@ interface PolicyModule { labelRes = R.string.quick_settings_flashlight_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.UTILITIES, ) /** Inject FlashlightTile into tileViewModelMap in QSModule */ @@ -192,7 +194,8 @@ interface PolicyModule { policy = QSTilePolicy.Restricted( listOf(DISALLOW_SHARE_LOCATION, DISALLOW_CONFIG_LOCATION) - ) + ), + category = TileCategory.PRIVACY, ) /** Inject LocationTile into tileViewModelMap in QSModule */ @@ -225,6 +228,7 @@ interface PolicyModule { labelRes = R.string.status_bar_alarm, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.UTILITIES, ) /** Inject AlarmTile into tileViewModelMap in QSModule */ @@ -257,6 +261,7 @@ interface PolicyModule { labelRes = R.string.quick_settings_ui_mode_night_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.DISPLAY, ) /** Inject uimodenight into tileViewModelMap in QSModule */ @@ -290,6 +295,7 @@ interface PolicyModule { ), instanceId = uiEventLogger.getNewInstanceId(), autoRemoveOnUnavailable = false, + category = TileCategory.PRIVACY, ) /** Inject work mode into tileViewModelMap in QSModule */ @@ -323,6 +329,7 @@ interface PolicyModule { ), instanceId = uiEventLogger.getNewInstanceId(), policy = QSTilePolicy.Restricted(listOf(DISALLOW_CAMERA_TOGGLE)), + category = TileCategory.PRIVACY, ) /** Inject camera toggle tile into tileViewModelMap in QSModule */ @@ -365,6 +372,7 @@ interface PolicyModule { ), instanceId = uiEventLogger.getNewInstanceId(), policy = QSTilePolicy.Restricted(listOf(DISALLOW_MICROPHONE_TOGGLE)), + category = TileCategory.PRIVACY, ) /** Inject microphone toggle tile into tileViewModelMap in QSModule */ @@ -407,6 +415,7 @@ interface PolicyModule { labelRes = R.string.quick_settings_modes_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.CONNECTIVITY, ) } else { QSTileConfig( @@ -417,6 +426,7 @@ interface PolicyModule { labelRes = R.string.quick_settings_dnd_label, ), instanceId = uiEventLogger.getNewInstanceId(), + category = TileCategory.CONNECTIVITY, ) } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java index ea213cba9567..dd1c11d11d1e 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java @@ -25,6 +25,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.pipeline.shared.TileSpec; +import com.android.systemui.qs.shared.model.TileCategory; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.qs.tiles.QuickAccessWalletTile; import com.android.systemui.qs.tiles.viewmodel.QSTileConfig; @@ -34,8 +35,6 @@ import com.android.systemui.res.R; import com.android.systemui.wallet.controller.WalletContextualLocationsService; import com.android.systemui.wallet.ui.WalletActivity; -import java.util.concurrent.Executor; - import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -43,6 +42,8 @@ import dagger.multibindings.ClassKey; import dagger.multibindings.IntoMap; import dagger.multibindings.StringKey; +import java.util.concurrent.Executor; + /** * Module for injecting classes in Wallet. */ @@ -90,6 +91,7 @@ public abstract class WalletModule { R.string.wallet_title ), uiEventLogger.getNewInstanceId(), + TileCategory.UTILITIES, tileSpec.getSpec(), QSTilePolicy.NoRestrictions.INSTANCE ); 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 d9faa30cb072..70af5e75105d 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 @@ -31,17 +31,18 @@ import androidx.compose.ui.test.onChildren import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.text.AnnotatedString import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon -import com.android.systemui.common.shared.model.Text import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -199,10 +200,11 @@ class DragAndDropTest : SysuiTestCase() { android.R.drawable.star_on, ContentDescription.Loaded(tileSpec) ), - label = Text.Loaded(tileSpec), + label = AnnotatedString(tileSpec), appName = null, isCurrent = true, availableEditActions = emptySet(), + category = TileCategory.UNKNOWN, ), getWidth(tileSpec), ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt index b03542cb569e..33227a4fcc62 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt @@ -16,6 +16,8 @@ package com.android.systemui.qs.panels.ui.viewmodel +import android.content.applicationContext +import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.qs.panels.domain.interactor.editTilesListInteractor @@ -33,6 +35,8 @@ val Kosmos.editModeViewModel by currentTilesInteractor, tilesAvailabilityInteractor, minimumTilesInteractor, + configurationInteractor, + applicationContext, infiniteGridLayout, applicationCoroutineScope, gridLayoutTypeInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt index dceb8bff0ae7..f66125a6087e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt @@ -20,6 +20,7 @@ import android.os.UserHandle import com.android.systemui.kosmos.Kosmos import com.android.systemui.qs.instanceIdSequenceFake import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileState @@ -47,6 +48,7 @@ val Kosmos.customTileViewModelFactory: QSTileViewModelFactory.Component by tileSpec, QSTileUIConfig.Empty, instanceIdSequenceFake.newInstanceId(), + category = TileCategory.PROVIDED_BY_APP, ) object : QSTileViewModel { override val state: StateFlow<QSTileState?> = diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt index 2a0ee888db35..73d9b3233375 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt @@ -18,6 +18,7 @@ package com.android.systemui.qs.tiles.viewmodel import com.android.internal.logging.InstanceId import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.shared.model.TileCategory object QSTileConfigTestBuilder { @@ -30,12 +31,14 @@ object QSTileConfigTestBuilder { var instanceId: InstanceId = InstanceId.fakeInstanceId(0) var metricsSpec: String = tileSpec.spec var policy: QSTilePolicy = QSTilePolicy.NoRestrictions + var category: TileCategory = TileCategory.UNKNOWN fun build() = QSTileConfig( tileSpec, uiConfig, instanceId, + category, metricsSpec, policy, ) |