Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * 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.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC import com.android.systemui.statusbar.policy.PolicyModule.Companion.WORK_MODE_TILE_SPEC import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @SmallTest class NewTilesAvailabilityInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().apply { tileAvailabilityInteractorsMap = buildMap { put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor) put(WORK_MODE_TILE_SPEC, FakeTileAvailabilityInteractor( mapOf( fakeUserRepository.getSelectedUserInfo().id to flowOf(true), ).withDefault { flowOf(false) } )) put(HOTSPOT_TILE_SPEC, FakeTileAvailabilityInteractor( emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) } )) } } private val underTest by lazy { kosmos.newTilesAvailabilityInteractor } @Test fun defaultUser_getAvailabilityFlow() = with(kosmos) { testScope.runTest { val availability by collectLastValue(underTest.newTilesAvailable) assertThat(availability).isEqualTo( mapOf( TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true, TileSpec.create(WORK_MODE_TILE_SPEC) to true, TileSpec.create(HOTSPOT_TILE_SPEC) to false, ) ) } } @Test fun getAvailabilityFlow_userChange() = with(kosmos) { testScope.runTest { val availability by collectLastValue(underTest.newTilesAvailable) fakeUserRepository.asMainUser() assertThat(availability).isEqualTo( mapOf( TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true, TileSpec.create(WORK_MODE_TILE_SPEC) to false, TileSpec.create(HOTSPOT_TILE_SPEC) to false, ) ) } } @Test fun noAvailabilityInteractor_emptyMap() = with(kosmos) { testScope.runTest { tileAvailabilityInteractorsMap = emptyMap() val availability by collectLastValue(underTest.newTilesAvailable) assertThat(availability).isEmpty() } } } packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt 0 → 100644 +228 −0 Original line number Diff line number Diff line /* * 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 android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_QS_NEW_TILES import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.qs.QSFactory import com.android.systemui.qs.FakeQSFactory import com.android.systemui.qs.FakeQSTile import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.qsTileFactory import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.INTERNET_TILE_SPEC import com.android.systemui.statusbar.policy.PolicyModule.Companion.FLASHLIGHT_TILE_SPEC import com.android.systemui.statusbar.policy.PolicyModule.Companion.WORK_MODE_TILE_SPEC import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.user.data.repository.userRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @RunWith(ParameterizedAndroidJunit4::class) @SmallTest class TilesAvailabilityInteractorTest(flags: FlagsParameterization) : SysuiTestCase() { init { mSetFlagsRule.setFlagsParameterization(flags) } private val createdTiles = mutableListOf<FakeQSTile>() private val kosmos = testKosmos().apply { tileAvailabilityInteractorsMap = buildMap { put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor) put(WORK_MODE_TILE_SPEC, FakeTileAvailabilityInteractor( mapOf( fakeUserRepository.getSelectedUserInfo().id to flowOf(true), ).withDefault { flowOf(false) } )) put(HOTSPOT_TILE_SPEC, FakeTileAvailabilityInteractor( emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) } )) } qsTileFactory = constantFactory( tilesForCreator( userRepository.getSelectedUserInfo().id, mapOf( AIRPLANE_MODE_TILE_SPEC to false, WORK_MODE_TILE_SPEC to false, HOTSPOT_TILE_SPEC to true, INTERNET_TILE_SPEC to true, FLASHLIGHT_TILE_SPEC to false, ) ) ) } private val underTest by lazy { kosmos.tilesAvailabilityInteractor } @Test @DisableFlags(FLAG_QS_NEW_TILES) fun flagOff_usesAvailabilityFromFactoryTiles() = with(kosmos) { testScope.runTest { val unavailableTiles = underTest.getUnavailableTiles( setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).map(TileSpec::create) ) assertThat(unavailableTiles).isEqualTo(setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).mapTo(mutableSetOf(), TileSpec::create)) } } @Test fun tileCannotBeCreated_isUnavailable() = with(kosmos) { testScope.runTest { val badSpec = TileSpec.create("unknown") val unavailableTiles = underTest.getUnavailableTiles( setOf( badSpec ) ) assertThat(unavailableTiles).contains(badSpec) } } @Test @EnableFlags(FLAG_QS_NEW_TILES) fun flagOn_defaultsToInteractorTiles_usesFactoryForOthers() = with(kosmos) { testScope.runTest { val unavailableTiles = underTest.getUnavailableTiles( setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).map(TileSpec::create) ) assertThat(unavailableTiles).isEqualTo(setOf( HOTSPOT_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).mapTo(mutableSetOf(), TileSpec::create)) } } @Test @EnableFlags(FLAG_QS_NEW_TILES) fun flagOn_defaultsToInteractorTiles_usesFactoryForOthers_userChange() = with(kosmos) { testScope.runTest { fakeUserRepository.asMainUser() val unavailableTiles = underTest.getUnavailableTiles( setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).map(TileSpec::create) ) assertThat(unavailableTiles).isEqualTo(setOf( WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).mapTo(mutableSetOf(), TileSpec::create)) } } @Test @EnableFlags(FLAG_QS_NEW_TILES) fun flagOn_onlyNeededTilesAreCreated_andThenDestroyed() = with(kosmos) { testScope.runTest { underTest.getUnavailableTiles( setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).map(TileSpec::create) ) assertThat(createdTiles.map { it.tileSpec }) .containsExactly(INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC) assertThat(createdTiles.all { it.destroyed }).isTrue() } } @Test @DisableFlags(FLAG_QS_NEW_TILES) fun flagOn_TilesAreCreatedAndThenDestroyed() = with(kosmos) { testScope.runTest { val allTiles = setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ) underTest.getUnavailableTiles(allTiles.map(TileSpec::create)) assertThat(createdTiles.map { it.tileSpec }) .containsExactlyElementsIn(allTiles) assertThat(createdTiles.all { it.destroyed }).isTrue() } } private fun constantFactory(creatorTiles: Set<FakeQSTile>): QSFactory { return FakeQSFactory { spec -> creatorTiles.firstOrNull { it.tileSpec == spec }?.also { createdTiles.add(it) } } } companion object { private fun tilesForCreator( user: Int, specAvailabilities: Map<String, Boolean> ): Set<FakeQSTile> { return specAvailabilities.mapTo(mutableSetOf()) { FakeQSTile(user, it.value).apply { tileSpec = it.key } } } @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf(FLAG_QS_NEW_TILES) } } } packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt +62 −33 Original line number Diff line number Diff line Loading @@ -19,30 +19,27 @@ package com.android.systemui.qs.panels.ui.viewmodel import android.R import android.content.ComponentName import android.graphics.drawable.TestStubDrawable import androidx.test.ext.junit.runners.AndroidJUnit4 import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.systemui.Flags 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.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.qs.FakeQSFactory import com.android.systemui.qs.FakeQSTile import com.android.systemui.qs.panels.data.repository.stockTilesRepository import com.android.systemui.qs.panels.domain.interactor.editTilesListInteractor import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout import com.android.systemui.qs.panels.domain.interactor.FakeTileAvailabilityInteractor import com.android.systemui.qs.panels.domain.interactor.tileAvailabilityInteractorsMap 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.MinimumTilesFixedRepository import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesRepository import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor import com.android.systemui.qs.pipeline.domain.interactor.minimumTilesInteractor import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.qsTileFactory import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig Loading @@ -57,14 +54,23 @@ import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider import com.android.systemui.settings.userTracker import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @RunWith(AndroidJUnit4::class) @RunWith(ParameterizedAndroidJunit4::class) @SmallTest class EditModeViewModelTest : SysuiTestCase() { class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { init { mSetFlagsRule.setFlagsParameterization(flags) } private val kosmos = testKosmos() // Only have some configurations so we can test the effect of missing configurations. Loading Loading @@ -98,17 +104,7 @@ class EditModeViewModelTest : SysuiTestCase() { ) private val underTest: EditModeViewModel by lazy { with(kosmos) { EditModeViewModel( editTilesListInteractor, currentTilesInteractor, minimumTilesInteractor, infiniteGridLayout, applicationCoroutineScope, gridLayoutTypeInteractor, gridLayoutMap, ) } kosmos.editModeViewModel } @Before Loading Loading @@ -464,18 +460,45 @@ class EditModeViewModelTest : SysuiTestCase() { } } private companion object { val drawable1 = TestStubDrawable("drawable1") val appName1 = "App1" val tileService1 = "Tile Service 1" val component1 = ComponentName("pkg1", "srv1") @Test fun tileNotAvailable_notShowing() = with(kosmos) { testScope.runTest { val unavailableTile = "work" qsTileFactory = FakeQSFactory { spec -> FakeQSTile(userTracker.userId, spec != unavailableTile) } tileAvailabilityInteractorsMap = mapOf( unavailableTile to FakeTileAvailabilityInteractor( emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) } ) ) val tiles by collectLastValue(underTest.tiles) val currentTiles = mutableListOf( TileSpec.create("flashlight"), TileSpec.create("airplane"), TileSpec.create("alarm"), ) currentTilesInteractor.setTiles(currentTiles) val drawable2 = TestStubDrawable("drawable2") val appName2 = "App2" val tileService2 = "Tile Service 2" val component2 = ComponentName("pkg2", "srv2") underTest.startEditing() fun TileSpec.missingConfigEditTileData(): EditTileData { assertThat(tiles!!.none { it.tileSpec == TileSpec.create(unavailableTile) }).isTrue() } } companion object { private val drawable1 = TestStubDrawable("drawable1") private val appName1 = "App1" private val tileService1 = "Tile Service 1" private val component1 = ComponentName("pkg1", "srv1") private val drawable2 = TestStubDrawable("drawable2") private val appName2 = "App2" private val tileService2 = "Tile Service 2" private val component2 = ComponentName("pkg2", "srv2") private fun TileSpec.missingConfigEditTileData(): EditTileData { return EditTileData( tileSpec = this, icon = Icon.Resource(R.drawable.star_on, ContentDescription.Loaded(spec)), Loading @@ -484,7 +507,7 @@ class EditModeViewModelTest : SysuiTestCase() { ) } fun QSTileConfig.toEditTileData(): EditTileData { private fun QSTileConfig.toEditTileData(): EditTileData { return EditTileData( tileSpec = tileSpec, icon = Loading @@ -494,7 +517,7 @@ class EditModeViewModelTest : SysuiTestCase() { ) } fun Kosmos.getEditTileData(tileSpec: TileSpec): EditTileData { private fun Kosmos.getEditTileData(tileSpec: TileSpec): EditTileData { return if (qSTileConfigProvider.hasConfig(tileSpec.spec)) { qSTileConfigProvider.getConfig(tileSpec.spec).toEditTileData() } else { Loading @@ -502,6 +525,12 @@ class EditModeViewModelTest : SysuiTestCase() { } } val minNumberOfTiles = 3 private val minNumberOfTiles = 3 @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf(Flags.FLAG_QS_NEW_TILES) } } } packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt +43 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.qs.tiles.HearingDevicesTile import com.android.systemui.qs.tiles.NightDisplayTile import com.android.systemui.qs.tiles.OneHandedModeTile import com.android.systemui.qs.tiles.ReduceBrightColorsTile 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.colorcorrection.domain.ColorCorrectionTileMapper import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor Loading Loading @@ -115,6 +116,48 @@ interface QSAccessibilityModule { @StringKey(HearingDevicesTile.TILE_SPEC) fun bindHearingDevicesTile(hearingDevicesTile: HearingDevicesTile): QSTileImpl<*> @Binds @IntoMap @StringKey(COLOR_CORRECTION_TILE_SPEC) fun provideColorCorrectionAvailabilityInteractor( impl: ColorCorrectionTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(COLOR_INVERSION_TILE_SPEC) fun provideColorInversionAvailabilityInteractor( impl: ColorCorrectionTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(FONT_SCALING_TILE_SPEC) fun provideFontScalingAvailabilityInteractor( impl: FontScalingTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(REDUCE_BRIGHTNESS_TILE_SPEC) fun provideReduceBrightnessAvailabilityInteractor( impl: ReduceBrightColorsTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(ONE_HANDED_TILE_SPEC) fun provideOneHandedAvailabilityInteractor( impl: OneHandedModeTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(NIGHT_DISPLAY_TILE_SPEC) fun provideNightDisplayAvailabilityInteractor( impl: NightDisplayTileDataInteractor ): QSTileAvailabilityInteractor companion object { const val COLOR_CORRECTION_TILE_SPEC = "color_correction" const val COLOR_INVERSION_TILE_SPEC = "inversion" Loading packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt +8 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.BatterySaverTile 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.battery.domain.interactor.BatterySaverTileDataInteractor import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileUserActionInteractor Loading @@ -28,6 +29,13 @@ interface BatterySaverModule { @StringKey(BatterySaverTile.TILE_SPEC) fun bindBatterySaverTile(batterySaverTile: BatterySaverTile): QSTileImpl<*> @Binds @IntoMap @StringKey(BATTERY_SAVER_TILE_SPEC) fun provideBatterySaverAvailabilityInteractor( impl: BatterySaverTileDataInteractor ): QSTileAvailabilityInteractor companion object { private const val BATTERY_SAVER_TILE_SPEC = "battery" Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * 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.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC import com.android.systemui.statusbar.policy.PolicyModule.Companion.WORK_MODE_TILE_SPEC import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @SmallTest class NewTilesAvailabilityInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().apply { tileAvailabilityInteractorsMap = buildMap { put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor) put(WORK_MODE_TILE_SPEC, FakeTileAvailabilityInteractor( mapOf( fakeUserRepository.getSelectedUserInfo().id to flowOf(true), ).withDefault { flowOf(false) } )) put(HOTSPOT_TILE_SPEC, FakeTileAvailabilityInteractor( emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) } )) } } private val underTest by lazy { kosmos.newTilesAvailabilityInteractor } @Test fun defaultUser_getAvailabilityFlow() = with(kosmos) { testScope.runTest { val availability by collectLastValue(underTest.newTilesAvailable) assertThat(availability).isEqualTo( mapOf( TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true, TileSpec.create(WORK_MODE_TILE_SPEC) to true, TileSpec.create(HOTSPOT_TILE_SPEC) to false, ) ) } } @Test fun getAvailabilityFlow_userChange() = with(kosmos) { testScope.runTest { val availability by collectLastValue(underTest.newTilesAvailable) fakeUserRepository.asMainUser() assertThat(availability).isEqualTo( mapOf( TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true, TileSpec.create(WORK_MODE_TILE_SPEC) to false, TileSpec.create(HOTSPOT_TILE_SPEC) to false, ) ) } } @Test fun noAvailabilityInteractor_emptyMap() = with(kosmos) { testScope.runTest { tileAvailabilityInteractorsMap = emptyMap() val availability by collectLastValue(underTest.newTilesAvailable) assertThat(availability).isEmpty() } } }
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt 0 → 100644 +228 −0 Original line number Diff line number Diff line /* * 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 android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_QS_NEW_TILES import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.qs.QSFactory import com.android.systemui.qs.FakeQSFactory import com.android.systemui.qs.FakeQSTile import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.qsTileFactory import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.INTERNET_TILE_SPEC import com.android.systemui.statusbar.policy.PolicyModule.Companion.FLASHLIGHT_TILE_SPEC import com.android.systemui.statusbar.policy.PolicyModule.Companion.WORK_MODE_TILE_SPEC import com.android.systemui.testKosmos import com.android.systemui.user.data.repository.fakeUserRepository import com.android.systemui.user.data.repository.userRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @RunWith(ParameterizedAndroidJunit4::class) @SmallTest class TilesAvailabilityInteractorTest(flags: FlagsParameterization) : SysuiTestCase() { init { mSetFlagsRule.setFlagsParameterization(flags) } private val createdTiles = mutableListOf<FakeQSTile>() private val kosmos = testKosmos().apply { tileAvailabilityInteractorsMap = buildMap { put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor) put(WORK_MODE_TILE_SPEC, FakeTileAvailabilityInteractor( mapOf( fakeUserRepository.getSelectedUserInfo().id to flowOf(true), ).withDefault { flowOf(false) } )) put(HOTSPOT_TILE_SPEC, FakeTileAvailabilityInteractor( emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) } )) } qsTileFactory = constantFactory( tilesForCreator( userRepository.getSelectedUserInfo().id, mapOf( AIRPLANE_MODE_TILE_SPEC to false, WORK_MODE_TILE_SPEC to false, HOTSPOT_TILE_SPEC to true, INTERNET_TILE_SPEC to true, FLASHLIGHT_TILE_SPEC to false, ) ) ) } private val underTest by lazy { kosmos.tilesAvailabilityInteractor } @Test @DisableFlags(FLAG_QS_NEW_TILES) fun flagOff_usesAvailabilityFromFactoryTiles() = with(kosmos) { testScope.runTest { val unavailableTiles = underTest.getUnavailableTiles( setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).map(TileSpec::create) ) assertThat(unavailableTiles).isEqualTo(setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).mapTo(mutableSetOf(), TileSpec::create)) } } @Test fun tileCannotBeCreated_isUnavailable() = with(kosmos) { testScope.runTest { val badSpec = TileSpec.create("unknown") val unavailableTiles = underTest.getUnavailableTiles( setOf( badSpec ) ) assertThat(unavailableTiles).contains(badSpec) } } @Test @EnableFlags(FLAG_QS_NEW_TILES) fun flagOn_defaultsToInteractorTiles_usesFactoryForOthers() = with(kosmos) { testScope.runTest { val unavailableTiles = underTest.getUnavailableTiles( setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).map(TileSpec::create) ) assertThat(unavailableTiles).isEqualTo(setOf( HOTSPOT_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).mapTo(mutableSetOf(), TileSpec::create)) } } @Test @EnableFlags(FLAG_QS_NEW_TILES) fun flagOn_defaultsToInteractorTiles_usesFactoryForOthers_userChange() = with(kosmos) { testScope.runTest { fakeUserRepository.asMainUser() val unavailableTiles = underTest.getUnavailableTiles( setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).map(TileSpec::create) ) assertThat(unavailableTiles).isEqualTo(setOf( WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).mapTo(mutableSetOf(), TileSpec::create)) } } @Test @EnableFlags(FLAG_QS_NEW_TILES) fun flagOn_onlyNeededTilesAreCreated_andThenDestroyed() = with(kosmos) { testScope.runTest { underTest.getUnavailableTiles( setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ).map(TileSpec::create) ) assertThat(createdTiles.map { it.tileSpec }) .containsExactly(INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC) assertThat(createdTiles.all { it.destroyed }).isTrue() } } @Test @DisableFlags(FLAG_QS_NEW_TILES) fun flagOn_TilesAreCreatedAndThenDestroyed() = with(kosmos) { testScope.runTest { val allTiles = setOf( AIRPLANE_MODE_TILE_SPEC, WORK_MODE_TILE_SPEC, HOTSPOT_TILE_SPEC, INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC, ) underTest.getUnavailableTiles(allTiles.map(TileSpec::create)) assertThat(createdTiles.map { it.tileSpec }) .containsExactlyElementsIn(allTiles) assertThat(createdTiles.all { it.destroyed }).isTrue() } } private fun constantFactory(creatorTiles: Set<FakeQSTile>): QSFactory { return FakeQSFactory { spec -> creatorTiles.firstOrNull { it.tileSpec == spec }?.also { createdTiles.add(it) } } } companion object { private fun tilesForCreator( user: Int, specAvailabilities: Map<String, Boolean> ): Set<FakeQSTile> { return specAvailabilities.mapTo(mutableSetOf()) { FakeQSTile(user, it.value).apply { tileSpec = it.key } } } @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf(FLAG_QS_NEW_TILES) } } }
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt +62 −33 Original line number Diff line number Diff line Loading @@ -19,30 +19,27 @@ package com.android.systemui.qs.panels.ui.viewmodel import android.R import android.content.ComponentName import android.graphics.drawable.TestStubDrawable import androidx.test.ext.junit.runners.AndroidJUnit4 import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.systemui.Flags 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.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.qs.FakeQSFactory import com.android.systemui.qs.FakeQSTile import com.android.systemui.qs.panels.data.repository.stockTilesRepository import com.android.systemui.qs.panels.domain.interactor.editTilesListInteractor import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout import com.android.systemui.qs.panels.domain.interactor.FakeTileAvailabilityInteractor import com.android.systemui.qs.panels.domain.interactor.tileAvailabilityInteractorsMap 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.MinimumTilesFixedRepository import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesRepository import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor import com.android.systemui.qs.pipeline.domain.interactor.minimumTilesInteractor import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.qsTileFactory import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig Loading @@ -57,14 +54,23 @@ import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider import com.android.systemui.settings.userTracker import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @RunWith(AndroidJUnit4::class) @RunWith(ParameterizedAndroidJunit4::class) @SmallTest class EditModeViewModelTest : SysuiTestCase() { class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { init { mSetFlagsRule.setFlagsParameterization(flags) } private val kosmos = testKosmos() // Only have some configurations so we can test the effect of missing configurations. Loading Loading @@ -98,17 +104,7 @@ class EditModeViewModelTest : SysuiTestCase() { ) private val underTest: EditModeViewModel by lazy { with(kosmos) { EditModeViewModel( editTilesListInteractor, currentTilesInteractor, minimumTilesInteractor, infiniteGridLayout, applicationCoroutineScope, gridLayoutTypeInteractor, gridLayoutMap, ) } kosmos.editModeViewModel } @Before Loading Loading @@ -464,18 +460,45 @@ class EditModeViewModelTest : SysuiTestCase() { } } private companion object { val drawable1 = TestStubDrawable("drawable1") val appName1 = "App1" val tileService1 = "Tile Service 1" val component1 = ComponentName("pkg1", "srv1") @Test fun tileNotAvailable_notShowing() = with(kosmos) { testScope.runTest { val unavailableTile = "work" qsTileFactory = FakeQSFactory { spec -> FakeQSTile(userTracker.userId, spec != unavailableTile) } tileAvailabilityInteractorsMap = mapOf( unavailableTile to FakeTileAvailabilityInteractor( emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) } ) ) val tiles by collectLastValue(underTest.tiles) val currentTiles = mutableListOf( TileSpec.create("flashlight"), TileSpec.create("airplane"), TileSpec.create("alarm"), ) currentTilesInteractor.setTiles(currentTiles) val drawable2 = TestStubDrawable("drawable2") val appName2 = "App2" val tileService2 = "Tile Service 2" val component2 = ComponentName("pkg2", "srv2") underTest.startEditing() fun TileSpec.missingConfigEditTileData(): EditTileData { assertThat(tiles!!.none { it.tileSpec == TileSpec.create(unavailableTile) }).isTrue() } } companion object { private val drawable1 = TestStubDrawable("drawable1") private val appName1 = "App1" private val tileService1 = "Tile Service 1" private val component1 = ComponentName("pkg1", "srv1") private val drawable2 = TestStubDrawable("drawable2") private val appName2 = "App2" private val tileService2 = "Tile Service 2" private val component2 = ComponentName("pkg2", "srv2") private fun TileSpec.missingConfigEditTileData(): EditTileData { return EditTileData( tileSpec = this, icon = Icon.Resource(R.drawable.star_on, ContentDescription.Loaded(spec)), Loading @@ -484,7 +507,7 @@ class EditModeViewModelTest : SysuiTestCase() { ) } fun QSTileConfig.toEditTileData(): EditTileData { private fun QSTileConfig.toEditTileData(): EditTileData { return EditTileData( tileSpec = tileSpec, icon = Loading @@ -494,7 +517,7 @@ class EditModeViewModelTest : SysuiTestCase() { ) } fun Kosmos.getEditTileData(tileSpec: TileSpec): EditTileData { private fun Kosmos.getEditTileData(tileSpec: TileSpec): EditTileData { return if (qSTileConfigProvider.hasConfig(tileSpec.spec)) { qSTileConfigProvider.getConfig(tileSpec.spec).toEditTileData() } else { Loading @@ -502,6 +525,12 @@ class EditModeViewModelTest : SysuiTestCase() { } } val minNumberOfTiles = 3 private val minNumberOfTiles = 3 @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { return FlagsParameterization.allCombinationsOf(Flags.FLAG_QS_NEW_TILES) } } }
packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt +43 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import com.android.systemui.qs.tiles.HearingDevicesTile import com.android.systemui.qs.tiles.NightDisplayTile import com.android.systemui.qs.tiles.OneHandedModeTile import com.android.systemui.qs.tiles.ReduceBrightColorsTile 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.colorcorrection.domain.ColorCorrectionTileMapper import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor Loading Loading @@ -115,6 +116,48 @@ interface QSAccessibilityModule { @StringKey(HearingDevicesTile.TILE_SPEC) fun bindHearingDevicesTile(hearingDevicesTile: HearingDevicesTile): QSTileImpl<*> @Binds @IntoMap @StringKey(COLOR_CORRECTION_TILE_SPEC) fun provideColorCorrectionAvailabilityInteractor( impl: ColorCorrectionTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(COLOR_INVERSION_TILE_SPEC) fun provideColorInversionAvailabilityInteractor( impl: ColorCorrectionTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(FONT_SCALING_TILE_SPEC) fun provideFontScalingAvailabilityInteractor( impl: FontScalingTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(REDUCE_BRIGHTNESS_TILE_SPEC) fun provideReduceBrightnessAvailabilityInteractor( impl: ReduceBrightColorsTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(ONE_HANDED_TILE_SPEC) fun provideOneHandedAvailabilityInteractor( impl: OneHandedModeTileDataInteractor ): QSTileAvailabilityInteractor @Binds @IntoMap @StringKey(NIGHT_DISPLAY_TILE_SPEC) fun provideNightDisplayAvailabilityInteractor( impl: NightDisplayTileDataInteractor ): QSTileAvailabilityInteractor companion object { const val COLOR_CORRECTION_TILE_SPEC = "color_correction" const val COLOR_INVERSION_TILE_SPEC = "inversion" Loading
packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt +8 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.qs.tiles.BatterySaverTile 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.battery.domain.interactor.BatterySaverTileDataInteractor import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileUserActionInteractor Loading @@ -28,6 +29,13 @@ interface BatterySaverModule { @StringKey(BatterySaverTile.TILE_SPEC) fun bindBatterySaverTile(batterySaverTile: BatterySaverTile): QSTileImpl<*> @Binds @IntoMap @StringKey(BATTERY_SAVER_TILE_SPEC) fun provideBatterySaverAvailabilityInteractor( impl: BatterySaverTileDataInteractor ): QSTileAvailabilityInteractor companion object { private const val BATTERY_SAVER_TILE_SPEC = "battery" Loading