Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1413f13 authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "Create new Edit Mode" into main

parents efeedcc5 2ccbc3c3
Loading
Loading
Loading
Loading
+165 −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.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
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.kosmos.mainCoroutineContext
import com.android.systemui.kosmos.testScope
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.data.repository.installedTilesRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@SmallTest
class IconAndNameCustomRepositoryTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    private val packageManager: PackageManager = kosmos.packageManager
    private val userTracker: FakeUserTracker =
        kosmos.fakeUserTracker.apply {
            whenever(userContext.packageManager).thenReturn(packageManager)
        }

    private val service1 =
        FakeInstalledTilesComponentRepository.ServiceInfo(
            component1,
            tileService1,
            drawable1,
            appName1,
        )

    private val service2 =
        FakeInstalledTilesComponentRepository.ServiceInfo(
            component2,
            tileService2,
            drawable2,
            appName2,
        )

    private val underTest =
        with(kosmos) {
            IconAndNameCustomRepository(
                installedTilesRepository,
                userTracker,
                mainCoroutineContext,
            )
        }

    @Before
    fun setUp() {
        kosmos.fakeInstalledTilesRepository.setInstalledServicesForUser(
            userTracker.userId,
            listOf(service1, service2)
        )
    }

    @Test
    fun loadDataForCurrentServices() =
        with(kosmos) {
            testScope.runTest {
                val editTileDataList = underTest.getCustomTileData()
                val expectedData1 =
                    EditTileData(
                        TileSpec.create(component1),
                        Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)),
                        Text.Loaded(tileService1),
                        Text.Loaded(appName1),
                    )
                val expectedData2 =
                    EditTileData(
                        TileSpec.create(component2),
                        Icon.Loaded(drawable2, ContentDescription.Loaded(tileService2)),
                        Text.Loaded(tileService2),
                        Text.Loaded(appName2),
                    )

                assertThat(editTileDataList).containsExactly(expectedData1, expectedData2)
            }
        }

    @Test
    fun loadDataForCurrentServices_otherCurrentUser_empty() =
        with(kosmos) {
            testScope.runTest {
                userTracker.set(listOf(UserInfo(11, "", 0)), 0)
                val editTileDataList = underTest.getCustomTileData()

                assertThat(editTileDataList).isEmpty()
            }
        }

    @Test
    fun loadDataForCurrentServices_serviceInfoWithNullIcon_notInList() =
        with(kosmos) {
            testScope.runTest {
                val serviceNullIcon =
                    FakeInstalledTilesComponentRepository.ServiceInfo(
                        component2,
                        tileService2,
                    )
                fakeInstalledTilesRepository.setInstalledServicesForUser(
                    userTracker.userId,
                    listOf(service1, serviceNullIcon)
                )

                val expectedData1 =
                    EditTileData(
                        TileSpec.create(component1),
                        Icon.Loaded(drawable1, ContentDescription.Loaded(tileService1)),
                        Text.Loaded(tileService1),
                        Text.Loaded(appName1),
                    )

                val editTileDataList = underTest.getCustomTileData()
                assertThat(editTileDataList).containsExactly(expectedData1)
            }
        }

    private companion object {
        val drawable1 = TestStubDrawable("drawable1")
        val appName1 = "App1"
        val tileService1 = "Tile Service 1"
        val component1 = ComponentName("pkg1", "srv1")

        val drawable2 = TestStubDrawable("drawable2")
        val appName2 = "App2"
        val tileService2 = "Tile Service 2"
        val component2 = ComponentName("pkg2", "srv2")
    }
}
+46 −0
Original line number Diff line number Diff line
@@ -14,48 +14,33 @@
 * limitations under the License.
 */

package com.android.systemui.qs.panels.domain.repository
package com.android.systemui.qs.panels.data.repository

import android.content.res.mainResources
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.qs.panels.data.repository.IconTilesRepositoryImpl
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class IconTilesRepositoryImplTest : SysuiTestCase() {
@SmallTest
class StockTilesRepositoryTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    private val underTest = IconTilesRepositoryImpl()
    private val underTest = StockTilesRepository(kosmos.mainResources)

    @Test
    fun iconTilesSpecsIsValid() = runTest {
        val tilesSpecs by collectLastValue(underTest.iconTilesSpecs)
        assertThat(tilesSpecs).isEqualTo(ICON_ONLY_TILES_SPECS)
    }

    companion object {
        private val ICON_ONLY_TILES_SPECS =
            setOf(
                TileSpec.create("airplane"),
                TileSpec.create("battery"),
                TileSpec.create("cameratoggle"),
                TileSpec.create("cast"),
                TileSpec.create("color_correction"),
                TileSpec.create("inversion"),
                TileSpec.create("saver"),
                TileSpec.create("dnd"),
                TileSpec.create("flashlight"),
                TileSpec.create("location"),
                TileSpec.create("mictoggle"),
                TileSpec.create("nfc"),
                TileSpec.create("night"),
                TileSpec.create("rotation")
            )
    fun stockTilesMatchesResources() {
        val expected =
            kosmos.mainResources
                .getString(R.string.quick_settings_tiles_stock)
                .split(",")
                .map(TileSpec::create)
        assertThat(underTest.stockTiles).isEqualTo(expected)
    }
}
+198 −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.content.ComponentName
import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
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.kosmos.testScope
import com.android.systemui.qs.panels.data.repository.iconAndNameCustomRepository
import com.android.systemui.qs.panels.data.repository.stockTilesRepository
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.tiles.impl.battery.qsBatterySaverTileConfig
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.fakeQSTileConfigProvider
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.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@SmallTest
class EditTilesListInteractorTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    // Only have some configurations so we can test the effect of missing configurations.
    // As the configurations are injected by dagger, we'll have all the existing configurations
    private val internetTileConfig = kosmos.qsInternetTileConfig
    private val flashlightTileConfig = kosmos.qsFlashlightTileConfig
    private val batteryTileConfig = kosmos.qsBatterySaverTileConfig

    private val serviceInfo =
        FakeInstalledTilesComponentRepository.ServiceInfo(
            component,
            tileName,
            icon,
            appName,
        )

    private val underTest =
        with(kosmos) {
            EditTilesListInteractor(
                stockTilesRepository,
                qSTileConfigProvider,
                iconAndNameCustomRepository,
            )
        }

    @Before
    fun setUp() {
        with(kosmos) {
            fakeInstalledTilesRepository.setInstalledServicesForUser(
                userTracker.userId,
                listOf(serviceInfo)
            )

            with(fakeQSTileConfigProvider) {
                putConfig(internetTileConfig.tileSpec, internetTileConfig)
                putConfig(flashlightTileConfig.tileSpec, flashlightTileConfig)
                putConfig(batteryTileConfig.tileSpec, batteryTileConfig)
            }
        }
    }

    @Test
    fun getTilesToEdit_stockTilesHaveNoAppName() =
        with(kosmos) {
            testScope.runTest {
                val editTiles = underTest.getTilesToEdit()

                assertThat(editTiles.stockTiles.all { it.appName == null }).isTrue()
            }
        }

    @Test
    fun getTilesToEdit_stockTilesAreAllPlatformSpecs() =
        with(kosmos) {
            testScope.runTest {
                val editTiles = underTest.getTilesToEdit()

                assertThat(editTiles.stockTiles.all { it.tileSpec is TileSpec.PlatformTileSpec })
                    .isTrue()
            }
        }

    @Test
    fun getTilesToEdit_stockTiles_sameOrderAsRepository() =
        with(kosmos) {
            testScope.runTest {
                val editTiles = underTest.getTilesToEdit()

                assertThat(editTiles.stockTiles.map { it.tileSpec })
                    .isEqualTo(stockTilesRepository.stockTiles)
            }
        }

    @Test
    fun getTilesToEdit_customTileData_matchesService() =
        with(kosmos) {
            testScope.runTest {
                val editTiles = underTest.getTilesToEdit()
                val expected =
                    EditTileData(
                        tileSpec = TileSpec.create(component),
                        icon = Icon.Loaded(icon, ContentDescription.Loaded(tileName)),
                        label = Text.Loaded(tileName),
                        appName = Text.Loaded(appName),
                    )

                assertThat(editTiles.customTiles).hasSize(1)
                assertThat(editTiles.customTiles[0]).isEqualTo(expected)
            }
        }

    @Test
    fun getTilesToEdit_tilesInConfigProvider_correctData() =
        with(kosmos) {
            testScope.runTest {
                val editTiles = underTest.getTilesToEdit()

                assertThat(
                        editTiles.stockTiles.first { it.tileSpec == internetTileConfig.tileSpec }
                    )
                    .isEqualTo(internetTileConfig.toEditTileData())
                assertThat(
                        editTiles.stockTiles.first { it.tileSpec == flashlightTileConfig.tileSpec }
                    )
                    .isEqualTo(flashlightTileConfig.toEditTileData())
                assertThat(editTiles.stockTiles.first { it.tileSpec == batteryTileConfig.tileSpec })
                    .isEqualTo(batteryTileConfig.toEditTileData())
            }
        }

    @Test
    fun getTilesToEdit_tilesNotInConfigProvider_useDefaultData() =
        with(kosmos) {
            testScope.runTest {
                underTest
                    .getTilesToEdit()
                    .stockTiles
                    .filterNot { qSTileConfigProvider.hasConfig(it.tileSpec.spec) }
                    .forEach { assertThat(it).isEqualTo(it.tileSpec.missingConfigEditTileData()) }
            }
        }

    private companion object {
        val component = ComponentName("pkg", "srv")
        const val tileName = "Tile Service"
        const val appName = "App"
        val icon = TestStubDrawable("icon")

        fun TileSpec.missingConfigEditTileData(): EditTileData {
            return EditTileData(
                tileSpec = this,
                icon = Icon.Resource(android.R.drawable.star_on, ContentDescription.Loaded(spec)),
                label = Text.Loaded(spec),
                appName = null
            )
        }

        fun QSTileConfig.toEditTileData(): EditTileData {
            return EditTileData(
                tileSpec = tileSpec,
                icon =
                    Icon.Resource(uiConfig.iconRes, ContentDescription.Resource(uiConfig.labelRes)),
                label = Text.Resource(uiConfig.labelRes),
                appName = null,
            )
        }
    }
}
+507 −0

File added.

Preview size limit exceeded, changes collapsed.

+17 −17
Original line number Diff line number Diff line
@@ -90,7 +90,7 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
    }

    @Test
    fun componentsLoadedOnStart() =
    fun servicesLoadedOnStart() =
        testScope.runTest {
            val userId = 0
            val resolveInfo =
@@ -106,12 +106,14 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {

            val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
            runCurrent()
            val services = underTest.getInstalledTilesServiceInfos(userId)

            assertThat(componentNames).containsExactly(TEST_COMPONENT)
            assertThat(services).containsExactly(resolveInfo.serviceInfo)
        }

    @Test
    fun componentAdded_foundAfterPackageChange() =
    fun serviceAdded_foundAfterPackageChange() =
        testScope.runTest {
            val userId = 0
            val resolveInfo =
@@ -132,12 +134,14 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
                .thenReturn(listOf(resolveInfo))
            kosmos.fakePackageChangeRepository.notifyChange(PackageChangeModel.Empty)
            runCurrent()
            val services = underTest.getInstalledTilesServiceInfos(userId)

            assertThat(componentNames).containsExactly(TEST_COMPONENT)
            assertThat(services).containsExactly(resolveInfo.serviceInfo)
        }

    @Test
    fun componentWithoutPermission_notValid() =
    fun serviceWithoutPermission_notValid() =
        testScope.runTest {
            val userId = 0
            val resolveInfo =
@@ -152,13 +156,15 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
                .thenReturn(listOf(resolveInfo))

            val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
            val services = underTest.getInstalledTilesServiceInfos(userId)
            runCurrent()

            assertThat(componentNames).isEmpty()
            assertThat(services).isEmpty()
        }

    @Test
    fun componentNotEnabled_notValid() =
    fun serviceNotEnabled_notValid() =
        testScope.runTest {
            val userId = 0
            val resolveInfo =
@@ -173,9 +179,11 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {
                .thenReturn(listOf(resolveInfo))

            val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
            val services = underTest.getInstalledTilesServiceInfos(userId)
            runCurrent()

            assertThat(componentNames).isEmpty()
            assertThat(services).isEmpty()
        }

    @Test
@@ -221,30 +229,22 @@ class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() {

            val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
            runCurrent()
            val service = underTest.getInstalledTilesServiceInfos(userId)

            assertThat(componentNames).containsExactly(TEST_COMPONENT)
            assertThat(service).containsExactly(resolveInfo.serviceInfo)
        }

    @Test
    fun loadComponentsForSameUserTwice_returnsSameFlow() =
    fun loadServicesForSameUserTwice_returnsSameFlow() =
        testScope.runTest {
            val flowForUser1 = underTest.getInstalledTilesComponents(1)
            val flowForUser1TheSecondTime = underTest.getInstalledTilesComponents(1)
            val flowForUser1 = underTest.getInstalledTilesServiceInfos(1)
            val flowForUser1TheSecondTime = underTest.getInstalledTilesServiceInfos(1)
            runCurrent()

            assertThat(flowForUser1TheSecondTime).isEqualTo(flowForUser1)
        }

    @Test
    fun loadComponentsForDifferentUsers_returnsDifferentFlow() =
        testScope.runTest {
            val flowForUser1 = underTest.getInstalledTilesComponents(1)
            val flowForUser2 = underTest.getInstalledTilesComponents(2)
            runCurrent()

            assertThat(flowForUser2).isNotEqualTo(flowForUser1)
        }

    // Tests that a ServiceInfo that is returned by queryIntentServicesAsUser but shortly
    // after uninstalled, doesn't crash SystemUI.
    @Test
Loading