Merge "Restore work tile position after restore" into main
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt
new file mode 100644
index 0000000..30d1822
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessorTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 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.pipeline.data.restoreprocessors
+
+import android.os.UserHandle
+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.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class WorkTileRestoreProcessorTest : SysuiTestCase() {
+
+ private val underTest = WorkTileRestoreProcessor()
+ @Test
+ fun restoreWithWorkTile_removeTracking() = runTest {
+ val removeTracking by collectLastValue(underTest.removeTrackingForUser(UserHandle.of(USER)))
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ restoredTiles = listOf(TILE_SPEC),
+ restoredAutoAddedTiles = setOf(TILE_SPEC),
+ USER,
+ )
+
+ underTest.postProcessRestore(restoreData)
+
+ assertThat(removeTracking).isEqualTo(Unit)
+ }
+
+ @Test
+ fun restoreWithWorkTile_otherUser_noRemoveTracking() = runTest {
+ val removeTracking by
+ collectLastValue(underTest.removeTrackingForUser(UserHandle.of(USER + 1)))
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ restoredTiles = listOf(TILE_SPEC),
+ restoredAutoAddedTiles = setOf(TILE_SPEC),
+ USER,
+ )
+
+ underTest.postProcessRestore(restoreData)
+
+ assertThat(removeTracking).isNull()
+ }
+
+ @Test
+ fun restoreWithoutWorkTile_noSignal() = runTest {
+ val removeTracking by collectLastValue(underTest.removeTrackingForUser(UserHandle.of(USER)))
+ runCurrent()
+
+ val restoreData =
+ RestoreData(
+ restoredTiles = emptyList(),
+ restoredAutoAddedTiles = emptySet(),
+ USER,
+ )
+
+ underTest.postProcessRestore(restoreData)
+
+ assertThat(removeTracking).isNull()
+ }
+
+ companion object {
+ private const val USER = 10
+ private val TILE_SPEC = TileSpec.Companion.create("work")
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index adccc84..c7e7845 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -25,6 +25,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
+import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -32,25 +37,28 @@
import com.android.systemui.settings.FakeUserTracker
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class WorkTileAutoAddableTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+
+ private val restoreProcessor: RestoreProcessor
+ get() = kosmos.workTileRestoreProcessor
+
private lateinit var userTracker: FakeUserTracker
private lateinit var underTest: WorkTileAutoAddable
@Before
fun setup() {
- MockitoAnnotations.initMocks(this)
-
userTracker =
FakeUserTracker(
_userId = USER_INFO_0.id,
@@ -58,7 +66,7 @@
_userProfiles = listOf(USER_INFO_0)
)
- underTest = WorkTileAutoAddable(userTracker)
+ underTest = WorkTileAutoAddable(userTracker, kosmos.workTileRestoreProcessor)
}
@Test
@@ -114,10 +122,80 @@
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Always)
}
+ @Test
+ fun restoreDataWithWorkTile_noCurrentManagedProfile_triggersRemove() = runTest {
+ val userId = 0
+ val signal by collectLastValue(underTest.autoAddSignal(userId))
+ runCurrent()
+
+ val restoreData = createRestoreWithWorkTile(userId)
+
+ restoreProcessor.postProcessRestore(restoreData)
+
+ assertThat(signal!!).isEqualTo(AutoAddSignal.RemoveTracking(SPEC))
+ }
+
+ @Test
+ fun restoreDataWithWorkTile_currentlyManagedProfile_doesntTriggerRemove() = runTest {
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ val userId = 0
+ val signals by collectValues(underTest.autoAddSignal(userId))
+ runCurrent()
+
+ val restoreData = createRestoreWithWorkTile(userId)
+
+ restoreProcessor.postProcessRestore(restoreData)
+
+ assertThat(signals).doesNotContain(AutoAddSignal.RemoveTracking(SPEC))
+ }
+
+ @Test
+ fun restoreDataWithoutWorkTile_noManagedProfile_doesntTriggerRemove() = runTest {
+ val userId = 0
+ val signals by collectValues(underTest.autoAddSignal(userId))
+ runCurrent()
+
+ val restoreData = createRestoreWithoutWorkTile(userId)
+
+ restoreProcessor.postProcessRestore(restoreData)
+
+ assertThat(signals).doesNotContain(AutoAddSignal.RemoveTracking(SPEC))
+ }
+
+ @Test
+ fun restoreDataWithoutWorkTile_managedProfile_doesntTriggerRemove() = runTest {
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ val userId = 0
+ val signals by collectValues(underTest.autoAddSignal(userId))
+ runCurrent()
+
+ val restoreData = createRestoreWithoutWorkTile(userId)
+
+ restoreProcessor.postProcessRestore(restoreData)
+
+ assertThat(signals).doesNotContain(AutoAddSignal.RemoveTracking(SPEC))
+ }
+
companion object {
private val SPEC = TileSpec.create(WorkModeTile.TILE_SPEC)
private val USER_INFO_0 = UserInfo(0, "", FLAG_PRIMARY or FLAG_FULL)
private val USER_INFO_1 = UserInfo(1, "", FLAG_FULL)
private val USER_INFO_WORK = UserInfo(10, "", FLAG_PROFILE or FLAG_MANAGED_PROFILE)
+
+ private fun createRestoreWithWorkTile(userId: Int): RestoreData {
+ return RestoreData(
+ listOf(TileSpec.create("a"), SPEC, TileSpec.create("b")),
+ setOf(SPEC),
+ userId,
+ )
+ }
+
+ private fun createRestoreWithoutWorkTile(userId: Int): RestoreData {
+ return RestoreData(
+ listOf(TileSpec.create("a"), TileSpec.create("b")),
+ emptySet(),
+ userId,
+ )
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
index 41a7ec0..54b03a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
@@ -183,6 +183,22 @@
assertThat(autoAddedTiles).contains(SPEC)
}
+ @Test
+ fun autoAddable_removeTrackingSignal_notRemovedButUnmarked() =
+ testScope.runTest {
+ autoAddRepository.markTileAdded(USER, SPEC)
+ val autoAddedTiles by collectLastValue(autoAddRepository.autoAddedTiles(USER))
+ val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.Always)
+
+ underTest = createInteractor(setOf(fakeAutoAddable))
+
+ fakeAutoAddable.sendRemoveTrackingSignal(USER)
+ runCurrent()
+
+ verify(currentTilesInteractor, never()).removeTiles(any())
+ assertThat(autoAddedTiles).doesNotContain(SPEC)
+ }
+
private fun createInteractor(autoAddables: Set<AutoAddable>): AutoAddInteractor {
return AutoAddInteractor(
autoAddables,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
index f73cab8..b2a9783 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
@@ -5,10 +5,15 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
import com.android.systemui.qs.pipeline.data.repository.FakeAutoAddRepository
import com.android.systemui.qs.pipeline.data.repository.FakeQSSettingsRestoredRepository
import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository
import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter
+import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractorTest.TestableRestoreProcessor.Companion.POSTPROCESS
+import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractorTest.TestableRestoreProcessor.Companion.PREPROCESS
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
@@ -17,7 +22,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.inOrder
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -28,6 +33,9 @@
private val qsSettingsRestoredRepository = FakeQSSettingsRestoredRepository()
+ private val restoreProcessor: TestableRestoreProcessor = TestableRestoreProcessor()
+ private val qsLogger: QSPipelineLogger = mock()
+
private lateinit var underTest: RestoreReconciliationInteractor
private val testDispatcher = StandardTestDispatcher()
@@ -35,13 +43,13 @@
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
underTest =
RestoreReconciliationInteractor(
tileSpecRepository,
autoAddRepository,
qsSettingsRestoredRepository,
+ setOf(restoreProcessor),
+ qsLogger,
testScope.backgroundScope,
testDispatcher
)
@@ -85,6 +93,44 @@
assertThat(autoAdd).isEqualTo(expectedAutoAdd.toTilesSet())
}
+ @Test
+ fun restoreProcessorsCalled() =
+ testScope.runTest {
+ val user = 10
+
+ val restoredSpecs = "a,c,d,f"
+ val restoredAutoAdded = "d,e"
+
+ val restoreData =
+ RestoreData(
+ restoredSpecs.toTilesList(),
+ restoredAutoAdded.toTilesSet(),
+ user,
+ )
+
+ qsSettingsRestoredRepository.onDataRestored(restoreData)
+ runCurrent()
+
+ assertThat(restoreProcessor.calls).containsExactly(PREPROCESS, POSTPROCESS).inOrder()
+ }
+
+ private class TestableRestoreProcessor : RestoreProcessor {
+ val calls = mutableListOf<Any>()
+
+ override suspend fun preProcessRestore(restoreData: RestoreData) {
+ calls.add(PREPROCESS)
+ }
+
+ override suspend fun postProcessRestore(restoreData: RestoreData) {
+ calls.add(POSTPROCESS)
+ }
+
+ companion object {
+ val PREPROCESS = Any()
+ val POSTPROCESS = Any()
+ }
+ }
+
companion object {
private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
new file mode 100644
index 0000000..96d5774
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2023 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.pipeline.domain.interactor
+
+import android.content.pm.UserInfo
+import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.FakeQSFactory
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.fakeRestoreRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeTileSpecRepository
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.settings.userTracker
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * This integration test is for testing the solution to b/314781280. In particular, there are two
+ * issues we want to verify after a restore of a device with a work profile and a work mode tile:
+ * * When the work profile is re-enabled in the target device, it is auto-added.
+ * * The tile is auto-added in the same position that it was in the restored device.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class WorkProfileAutoAddedAfterRestoreTest : SysuiTestCase() {
+
+ private val kosmos = Kosmos().apply { fakeUserTracker.set(listOf(USER_0_INFO), 0) }
+ // Getter here so it can change when there is a managed profile.
+ private val workTileAvailable: Boolean
+ get() = hasManagedProfile()
+ private val currentUser: Int
+ get() = kosmos.userTracker.userId
+
+ private val testScope: TestScope
+ get() = kosmos.testScope
+
+ @Before
+ fun setUp() {
+ mSetFlagsRule.enableFlags(FLAG_QS_NEW_PIPELINE)
+
+ kosmos.qsTileFactory = FakeQSFactory(::tileCreator)
+ kosmos.restoreReconciliationInteractor.start()
+ kosmos.autoAddInteractor.init(kosmos.currentTilesInteractor)
+ }
+
+ @Test
+ fun workTileRestoredAndPreviouslyAutoAdded_notAvailable_willBeAutoaddedInCorrectPosition() =
+ testScope.runTest {
+ val tiles by collectLastValue(kosmos.currentTilesInteractor.currentTiles)
+
+ // Set up
+ val currentTiles = listOf("a".toTileSpec())
+ kosmos.fakeTileSpecRepository.setTiles(currentUser, currentTiles)
+
+ val restoredTiles =
+ listOf(WORK_TILE_SPEC) + listOf("b", "c", "d").map { it.toTileSpec() }
+ val restoredAutoAdded = setOf(WORK_TILE_SPEC)
+
+ val restoreData = RestoreData(restoredTiles, restoredAutoAdded, currentUser)
+
+ // WHEN we restore tiles that auto-added the WORK tile and it's not available (there
+ // are no managed profiles)
+ kosmos.fakeRestoreRepository.onDataRestored(restoreData)
+
+ // THEN the work tile is not part of the current tiles
+ assertThat(tiles!!).hasSize(3)
+ assertThat(tiles!!.map { it.spec }).doesNotContain(WORK_TILE_SPEC)
+
+ // WHEN we add a work profile
+ createManagedProfileAndAdd()
+
+ // THEN the work profile is added in the correct place
+ assertThat(tiles!!.first().spec).isEqualTo(WORK_TILE_SPEC)
+ }
+
+ @Test
+ fun workTileNotRestoredAndPreviouslyAutoAdded_wontBeAutoAddedWhenWorkProfileIsAdded() =
+ testScope.runTest {
+ val tiles by collectLastValue(kosmos.currentTilesInteractor.currentTiles)
+
+ // Set up
+ val currentTiles = listOf("a".toTileSpec())
+ kosmos.fakeTileSpecRepository.setTiles(currentUser, currentTiles)
+ runCurrent()
+
+ val restoredTiles = listOf("b", "c", "d").map { it.toTileSpec() }
+ val restoredAutoAdded = setOf(WORK_TILE_SPEC)
+
+ val restoreData = RestoreData(restoredTiles, restoredAutoAdded, currentUser)
+
+ // WHEN we restore tiles that auto-added the WORK tile
+ kosmos.fakeRestoreRepository.onDataRestored(restoreData)
+
+ // THEN the work tile is not part of the current tiles
+ assertThat(tiles!!).hasSize(3)
+ assertThat(tiles!!.map { it.spec }).doesNotContain(WORK_TILE_SPEC)
+
+ // WHEN we add a work profile
+ createManagedProfileAndAdd()
+
+ // THEN the work profile is not added because the user had manually removed it in the
+ // past
+ assertThat(tiles!!.map { it.spec }).doesNotContain(WORK_TILE_SPEC)
+ }
+
+ private fun tileCreator(spec: String): QSTile {
+ return if (spec == WORK_TILE_SPEC.spec) {
+ FakeQSTile(currentUser, workTileAvailable)
+ } else {
+ FakeQSTile(currentUser)
+ }
+ }
+
+ private fun hasManagedProfile(): Boolean {
+ return kosmos.userTracker.userProfiles.any { it.isManagedProfile }
+ }
+
+ private fun TestScope.createManagedProfileAndAdd() {
+ kosmos.fakeUserTracker.set(
+ listOf(USER_0_INFO, MANAGED_USER_INFO),
+ 0,
+ )
+ runCurrent()
+ }
+
+ private companion object {
+ val WORK_TILE_SPEC = "work".toTileSpec()
+ val USER_0_INFO =
+ UserInfo(
+ 0,
+ "zero",
+ "",
+ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ )
+ val MANAGED_USER_INFO =
+ UserInfo(
+ 10,
+ "ten-managed",
+ "",
+ 0,
+ UserManager.USER_TYPE_PROFILE_MANAGED,
+ )
+
+ fun String.toTileSpec() = TileSpec.create(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index b50798e..4bad45f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -20,6 +20,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
import com.android.systemui.qs.pipeline.data.repository.DefaultTilesQSHostRepository
import com.android.systemui.qs.pipeline.data.repository.DefaultTilesRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
@@ -39,14 +40,17 @@
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import dagger.multibindings.Multibinds
-@Module(includes = [QSAutoAddModule::class])
+@Module(includes = [QSAutoAddModule::class, RestoreProcessorsModule::class])
abstract class QSPipelineModule {
/** Implementation for [TileSpecRepository] */
@Binds
abstract fun provideTileSpecRepository(impl: TileSpecSettingsRepository): TileSpecRepository
+ @Multibinds abstract fun provideRestoreProcessors(): Set<RestoreProcessor>
+
@Binds
abstract fun provideDefaultTilesRepository(
impl: DefaultTilesQSHostRepository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/RestoreProcessorsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/RestoreProcessorsModule.kt
new file mode 100644
index 0000000..e970c84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/RestoreProcessorsModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.pipeline.dagger
+
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
+import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+interface RestoreProcessorsModule {
+
+ @Binds
+ @IntoSet
+ fun bindWorkTileRestoreProcessor(impl: WorkTileRestoreProcessor): RestoreProcessor
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessor.kt
new file mode 100644
index 0000000..8f7de19
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessor.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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.pipeline.data.model
+
+/**
+ * Perform processing of the [RestoreData] before or after it's applied to repositories.
+ *
+ * The order in which the restore processors are applied in not deterministic.
+ *
+ * In order to declare a restore processor, add it in [RestoreProcessingModule] using
+ *
+ * ```
+ * @Binds
+ * @IntoSet
+ * ``
+ */
+interface RestoreProcessor {
+
+ /** Should be called before applying the restore to the necessary repositories */
+ suspend fun preProcessRestore(restoreData: RestoreData) {}
+
+ /** Should be called after requesting the repositories to update. */
+ suspend fun postProcessRestore(restoreData: RestoreData) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
index 7998dfb..d40f3f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
@@ -20,9 +20,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
-import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
/** Repository to track what QS tiles have been auto-added */
interface AutoAddRepository {
@@ -49,8 +48,9 @@
@SysUISingleton
class AutoAddSettingRepository
@Inject
-constructor(private val userAutoAddRepositoryFactory: UserAutoAddRepository.Factory) :
- AutoAddRepository {
+constructor(
+ private val userAutoAddRepositoryFactory: UserAutoAddRepository.Factory,
+) : AutoAddRepository {
private val userAutoAddRepositories = SparseArray<UserAutoAddRepository>()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
index 6cee116..e718eea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
@@ -10,6 +10,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository.Companion.BUFFER_CAPACITY
import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter.toTilesList
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import javax.inject.Inject
@@ -28,6 +29,14 @@
/** Provides restored data (from Backup and Restore) for Quick Settings pipeline */
interface QSSettingsRestoredRepository {
val restoreData: Flow<RestoreData>
+
+ companion object {
+ // This capacity is the number of restore data that we will keep buffered in the shared
+ // flow. It is unlikely that at any given time there would be this many restores being
+ // processed by consumers, but just in case that a couple of users are restored at the
+ // same time and they need to be replayed for the consumers of the flow.
+ const val BUFFER_CAPACITY = 10
+ }
}
@SysUISingleton
@@ -86,11 +95,6 @@
private companion object {
private const val TAG = "QSSettingsRestoredBroadcastRepository"
- // This capacity is the number of restore data that we will keep buffered in the shared
- // flow. It is unlikely that at any given time there would be this many restores being
- // processed by consumers, but just in case that a couple of users are restored at the
- // same time and they need to be replayed for the consumers of the flow.
- private const val BUFFER_CAPACITY = 10
private val INTENT_FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
private const val TILES_SETTING = Settings.Secure.QS_TILES
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessor.kt
new file mode 100644
index 0000000..7376aa9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/restoreprocessors/WorkTileRestoreProcessor.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 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.pipeline.data.restoreprocessors
+
+import android.os.UserHandle
+import android.util.SparseIntArray
+import androidx.annotation.GuardedBy
+import androidx.core.util.getOrDefault
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
+import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.WorkModeTile
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+/**
+ * Processor for restore data for work tile.
+ *
+ * It will indicate when auto-add tracking may be removed for a user. This may be necessary if the
+ * tile will be destroyed due to being not available, but needs to be added once work profile is
+ * enabled (after restore), in the same position as it was in the restored data.
+ */
+@SysUISingleton
+class WorkTileRestoreProcessor @Inject constructor() : RestoreProcessor {
+
+ @GuardedBy("lastRestorePosition") private val lastRestorePosition = SparseIntArray()
+
+ private val _removeTrackingForUser =
+ MutableSharedFlow<Int>(extraBufferCapacity = QSSettingsRestoredRepository.BUFFER_CAPACITY)
+
+ /**
+ * Flow indicating that we may need to remove auto-add tracking for the work tile for a given
+ * user.
+ */
+ fun removeTrackingForUser(userHandle: UserHandle): Flow<Unit> {
+ return _removeTrackingForUser.filter { it == userHandle.identifier }.map {}
+ }
+
+ override suspend fun postProcessRestore(restoreData: RestoreData) {
+ if (TILE_SPEC in restoreData.restoredTiles) {
+ synchronized(lastRestorePosition) {
+ lastRestorePosition.put(
+ restoreData.userId,
+ restoreData.restoredTiles.indexOf(TILE_SPEC)
+ )
+ }
+ _removeTrackingForUser.emit(restoreData.userId)
+ }
+ }
+
+ fun pollLastPosition(userId: Int): Int {
+ return synchronized(lastRestorePosition) {
+ lastRestorePosition.getOrDefault(userId, TileSpecRepository.POSITION_AT_END).also {
+ lastRestorePosition.delete(userId)
+ }
+ }
+ }
+
+ companion object {
+ private val TILE_SPEC = TileSpec.create(WorkModeTile.TILE_SPEC)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
index 5e3c348..b221199 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
@@ -17,8 +17,10 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
import android.content.pm.UserInfo
+import android.os.UserHandle
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
@@ -28,6 +30,8 @@
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
/**
* [AutoAddable] for [WorkModeTile.TILE_SPEC].
@@ -36,17 +40,37 @@
* signal to remove it if there is not.
*/
@SysUISingleton
-class WorkTileAutoAddable @Inject constructor(private val userTracker: UserTracker) : AutoAddable {
+class WorkTileAutoAddable
+@Inject
+constructor(
+ private val userTracker: UserTracker,
+ private val workTileRestoreProcessor: WorkTileRestoreProcessor,
+) : AutoAddable {
private val spec = TileSpec.create(WorkModeTile.TILE_SPEC)
override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
- return conflatedCallbackFlow {
+ val removeTrackingDueToRestore: Flow<AutoAddSignal> =
+ workTileRestoreProcessor.removeTrackingForUser(UserHandle.of(userId)).mapNotNull {
+ val profiles = userTracker.userProfiles
+ if (profiles.any { it.id == userId } && !profiles.any { it.isManagedProfile }) {
+ // Only remove auto-added if there are no managed profiles for this user
+ AutoAddSignal.RemoveTracking(spec)
+ } else {
+ null
+ }
+ }
+ val signalsFromCallback = conflatedCallbackFlow {
fun maybeSend(profiles: List<UserInfo>) {
if (profiles.any { it.id == userId }) {
// We are looking at the profiles of the correct user.
if (profiles.any { it.isManagedProfile }) {
- trySend(AutoAddSignal.Add(spec))
+ trySend(
+ AutoAddSignal.Add(
+ spec,
+ workTileRestoreProcessor.pollLastPosition(userId),
+ )
+ )
} else {
trySend(AutoAddSignal.Remove(spec))
}
@@ -65,6 +89,7 @@
awaitClose { userTracker.removeCallback(callback) }
}
+ return merge(removeTrackingDueToRestore, signalsFromCallback)
}
override val autoAddTracking = AutoAddTracking.Always
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt
index b747393..cde3835 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt
@@ -103,6 +103,10 @@
qsPipelineLogger.logTileAutoRemoved(userId, signal.spec)
repository.unmarkTileAdded(userId, signal.spec)
}
+ is AutoAddSignal.RemoveTracking -> {
+ qsPipelineLogger.logTileUnmarked(userId, signal.spec)
+ repository.unmarkTileAdded(userId, signal.spec)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
index 9844903..a5be14e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
@@ -3,14 +3,15 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreProcessor
import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.take
@@ -33,6 +34,8 @@
private val tileSpecRepository: TileSpecRepository,
private val autoAddRepository: AutoAddRepository,
private val qsSettingsRestoredRepository: QSSettingsRestoredRepository,
+ private val restoreProcessors: Set<@JvmSuppressWildcards RestoreProcessor>,
+ private val qsPipelineLogger: QSPipelineLogger,
@Application private val applicationScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) {
@@ -40,14 +43,30 @@
@OptIn(ExperimentalCoroutinesApi::class)
fun start() {
applicationScope.launch(backgroundDispatcher) {
- qsSettingsRestoredRepository.restoreData.flatMapConcat { data ->
- autoAddRepository.autoAddedTiles(data.userId)
- .take(1)
- .map { tiles -> data to tiles }
- }.collect { (restoreData, autoAdded) ->
- tileSpecRepository.reconcileRestore(restoreData, autoAdded)
- autoAddRepository.reconcileRestore(restoreData)
- }
+ qsSettingsRestoredRepository.restoreData
+ .flatMapConcat { data ->
+ autoAddRepository.autoAddedTiles(data.userId).take(1).map { tiles ->
+ data to tiles
+ }
+ }
+ .collect { (restoreData, autoAdded) ->
+ restoreProcessors.forEach {
+ it.preProcessRestore(restoreData)
+ qsPipelineLogger.logRestoreProcessorApplied(
+ it::class.simpleName,
+ QSPipelineLogger.RestorePreprocessorStep.PREPROCESSING,
+ )
+ }
+ tileSpecRepository.reconcileRestore(restoreData, autoAdded)
+ autoAddRepository.reconcileRestore(restoreData)
+ restoreProcessors.forEach {
+ it.postProcessRestore(restoreData)
+ qsPipelineLogger.logRestoreProcessorApplied(
+ it::class.simpleName,
+ QSPipelineLogger.RestorePreprocessorStep.POSTPROCESSING,
+ )
+ }
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt
index ed7b8bd..8263680 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/model/AutoAddSignal.kt
@@ -34,4 +34,9 @@
data class Remove(
override val spec: TileSpec,
) : AutoAddSignal
+
+ /** Signal for remove the auto-add marker from the tile, but not remove the tile */
+ data class RemoveTracking(
+ override val spec: TileSpec,
+ ) : AutoAddSignal
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
index bca86c9..7d2c6c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
@@ -209,6 +209,18 @@
)
}
+ fun logTileUnmarked(userId: Int, spec: TileSpec) {
+ tileAutoAddLogBuffer.log(
+ AUTO_ADD_TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = userId
+ str1 = spec.toString()
+ },
+ { "Tile $str1 unmarked as auto-added for user $int1" }
+ )
+ }
+
fun logSettingsRestored(restoreData: RestoreData) {
restoreLogBuffer.log(
RESTORE_TAG,
@@ -226,6 +238,21 @@
)
}
+ fun logRestoreProcessorApplied(
+ restoreProcessorClassName: String?,
+ step: RestorePreprocessorStep,
+ ) {
+ restoreLogBuffer.log(
+ RESTORE_TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = restoreProcessorClassName
+ str2 = step.name
+ },
+ { "Restore $str2 processed by $str1" }
+ )
+ }
+
/** Reasons for destroying an existing tile. */
enum class TileDestroyedReason(val readable: String) {
TILE_REMOVED("Tile removed from current set"),
@@ -234,4 +261,9 @@
EXISTING_TILE_NOT_AVAILABLE("Existing tile not available"),
TILE_NOT_PRESENT_IN_NEW_USER("Tile not present in new user"),
}
+
+ enum class RestorePreprocessorStep {
+ PREPROCESSING,
+ POSTPROCESSING
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
index 1cb2587..6332c1a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
@@ -19,9 +19,14 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.qs.QSFactory
+import com.android.systemui.qs.tiles.di.NewQSTileFactory
val Kosmos.instanceIdSequenceFake: InstanceIdSequenceFake by
Kosmos.Fixture { InstanceIdSequenceFake(0) }
val Kosmos.uiEventLogger: UiEventLoggerFake by Kosmos.Fixture { UiEventLoggerFake() }
val Kosmos.qsEventLogger: QsEventLoggerFake by
Kosmos.Fixture { QsEventLoggerFake(uiEventLogger, instanceIdSequenceFake) }
+
+var Kosmos.newQSTileFactory by Kosmos.Fixture<NewQSTileFactory>()
+var Kosmos.qsTileFactory by Kosmos.Fixture<QSFactory>()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/CustomTileStatePersisterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/CustomTileStatePersisterKosmos.kt
new file mode 100644
index 0000000..f01e3aa
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/CustomTileStatePersisterKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 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.external
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.customTileStatePersister: CustomTileStatePersister by
+ Kosmos.Fixture { fakeCustomTileStatePersister }
+val Kosmos.fakeCustomTileStatePersister by Kosmos.Fixture { FakeCustomTileStatePersister() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt
new file mode 100644
index 0000000..f8ce707
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2023 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.external
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+/** Returns mocks */
+var Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by
+ Kosmos.Fixture { TileLifecycleManager.Factory { _, _ -> mock<TileLifecycleManager>() } }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessorsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessorsKosmos.kt
new file mode 100644
index 0000000..d93dd8d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/model/RestoreProcessorsKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 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.pipeline.data.model
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.pipeline.data.restoreprocessors.WorkTileRestoreProcessor
+
+val Kosmos.workTileRestoreProcessor by Kosmos.Fixture { WorkTileRestoreProcessor() }
+
+var Kosmos.restoreProcessors by
+ Kosmos.Fixture {
+ setOf(
+ workTileRestoreProcessor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
new file mode 100644
index 0000000..0091482
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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.pipeline.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeTileSpecRepository by Kosmos.Fixture { FakeTileSpecRepository() }
+var Kosmos.tileSpecRepository: TileSpecRepository by Kosmos.Fixture { fakeTileSpecRepository }
+
+val Kosmos.fakeAutoAddRepository by Kosmos.Fixture { FakeAutoAddRepository() }
+var Kosmos.autoAddRepository: AutoAddRepository by Kosmos.Fixture { fakeAutoAddRepository }
+
+val Kosmos.fakeRestoreRepository by Kosmos.Fixture { FakeQSSettingsRestoredRepository() }
+var Kosmos.restoreRepository: QSSettingsRestoredRepository by
+ Kosmos.Fixture { fakeRestoreRepository }
+
+val Kosmos.fakeInstalledTilesRepository by
+ Kosmos.Fixture { FakeInstalledTilesComponentRepository() }
+var Kosmos.installedTilesRepository: InstalledTilesComponentRepository by
+ Kosmos.Fixture { fakeInstalledTilesRepository }
+
+val Kosmos.fakeCustomTileAddedRepository by Kosmos.Fixture { FakeCustomTileAddedRepository() }
+var Kosmos.customTileAddedRepository: CustomTileAddedRepository by
+ Kosmos.Fixture { fakeCustomTileAddedRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddablesKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddablesKosmos.kt
new file mode 100644
index 0000000..35f178b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddablesKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 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.pipeline.domain.autoaddable
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.pipeline.data.model.workTileRestoreProcessor
+import com.android.systemui.settings.userTracker
+
+val Kosmos.workTileAutoAddable by
+ Kosmos.Fixture { WorkTileAutoAddable(userTracker, workTileRestoreProcessor) }
+
+var Kosmos.autoAddables by Kosmos.Fixture { setOf(workTileAutoAddable) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt
index ebdd6fd..bf8f4da 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/autoaddable/FakeAutoAddable.kt
@@ -39,14 +39,18 @@
return getFlow(userId).asStateFlow().filterNotNull()
}
- suspend fun sendRemoveSignal(userId: Int) {
+ fun sendRemoveSignal(userId: Int) {
getFlow(userId).value = AutoAddSignal.Remove(spec)
}
- suspend fun sendAddSignal(userId: Int, position: Int = POSITION_AT_END) {
+ fun sendAddSignal(userId: Int, position: Int = POSITION_AT_END) {
getFlow(userId).value = AutoAddSignal.Add(spec, position)
}
+ fun sendRemoveTrackingSignal(userId: Int) {
+ getFlow(userId).value = AutoAddSignal.RemoveTracking(spec)
+ }
+
override val description: String
get() = "FakeAutoAddable($spec)"
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorKosmos.kt
new file mode 100644
index 0000000..5e8471c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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.pipeline.domain.interactor
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.qs.pipeline.data.repository.autoAddRepository
+import com.android.systemui.qs.pipeline.domain.autoaddable.autoAddables
+import com.android.systemui.qs.pipeline.shared.logging.qsLogger
+
+val Kosmos.autoAddInteractor by
+ Kosmos.Fixture {
+ AutoAddInteractor(
+ autoAddables,
+ autoAddRepository,
+ dumpManager,
+ qsLogger,
+ applicationCoroutineScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
new file mode 100644
index 0000000..67df563
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 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.pipeline.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.qs.external.customTileStatePersister
+import com.android.systemui.qs.external.tileLifecycleManagerFactory
+import com.android.systemui.qs.newQSTileFactory
+import com.android.systemui.qs.pipeline.data.repository.customTileAddedRepository
+import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
+import com.android.systemui.qs.pipeline.shared.logging.qsLogger
+import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.settings.userTracker
+import com.android.systemui.user.data.repository.userRepository
+
+val Kosmos.currentTilesInteractor: CurrentTilesInteractor by
+ Kosmos.Fixture {
+ CurrentTilesInteractorImpl(
+ tileSpecRepository,
+ installedTilesRepository,
+ userRepository,
+ customTileStatePersister,
+ { newQSTileFactory },
+ qsTileFactory,
+ customTileAddedRepository,
+ tileLifecycleManagerFactory,
+ userTracker,
+ testDispatcher,
+ testDispatcher,
+ applicationCoroutineScope,
+ qsLogger,
+ pipelineFlagsRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorKosmos.kt
new file mode 100644
index 0000000..55c23d4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2023 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.pipeline.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.qs.pipeline.data.model.restoreProcessors
+import com.android.systemui.qs.pipeline.data.repository.autoAddRepository
+import com.android.systemui.qs.pipeline.data.repository.restoreRepository
+import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
+import com.android.systemui.qs.pipeline.shared.logging.qsLogger
+
+val Kosmos.restoreReconciliationInteractor by
+ Kosmos.Fixture {
+ RestoreReconciliationInteractor(
+ tileSpecRepository,
+ autoAddRepository,
+ restoreRepository,
+ restoreProcessors,
+ qsLogger,
+ applicationCoroutineScope,
+ testDispatcher,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagRepositoryKosmos.kt
new file mode 100644
index 0000000..961545a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2023 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.pipeline.shared
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.pipelineFlagsRepository by Kosmos.Fixture { QSPipelineFlagsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLoggerKosmos.kt
new file mode 100644
index 0000000..7d52f5d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLoggerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 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.pipeline.shared.logging
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+/** mock */
+var Kosmos.qsLogger: QSPipelineLogger by Kosmos.Fixture { mock<QSPipelineLogger>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index 7494ccf..2ca338a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -72,6 +72,7 @@
onBeforeUserSwitching()
onUserChanging()
onUserChanged()
+ onProfileChanged()
}
fun onBeforeUserSwitching(userId: Int = _userId) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserTrackerKosmos.kt
new file mode 100644
index 0000000..ffa86ff
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/UserTrackerKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 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.settings
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeUserTracker by Kosmos.Fixture { FakeUserTracker() }
+var Kosmos.userTracker: UserTracker by Kosmos.Fixture { fakeUserTracker }