From bd1e439025ee0be02978ad5e824022fdf37bb427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Kozynski?= Date: Fri, 16 Jun 2023 10:48:55 -0400 Subject: [PATCH] Add a AutoAddInteractor This is meant to replace the old AutoTileManager. It works with a collection of AutoAddable, so adding new rules for tiles that need to be added is easier to do. Fixes: 278577795 Test: atest com.android.systemui.qs.pipeline Flag: QS_PIPELINE_AUTO_ADD Change-Id: Iee7e70f90034c5bb773a515660f88548d5a0aa86 --- .../com/android/systemui/qs/QSTileHost.java | 4 +- .../qs/pipeline/dagger/QSAutoAddLog.kt | 22 ++ .../qs/pipeline/dagger/QSAutoAddModule.kt | 29 ++- .../qs/pipeline/dagger/QSTileListLog.kt | 2 +- .../domain/interactor/AutoAddInteractor.kt | 123 +++++++++++ .../interactor/CurrentTilesInteractor.kt | 5 + .../shared/logging/QSPipelineLogger.kt | 28 +++ .../android/systemui/qs/QSTileHostTest.java | 1 + .../interactor/AutoAddInteractorTest.kt | 201 ++++++++++++++++++ .../CurrentTilesInteractorImplTest.kt | 9 + .../data/repository/FakeAutoAddRepository.kt | 42 ++++ 11 files changed, 463 insertions(+), 3 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSAutoAddLog.kt create mode 100644 packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt create mode 100644 packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index d2568ac79105a..bc4d774995b5a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -161,7 +161,9 @@ public class QSTileHost implements QSHost, Tunable, PluginListener, P // finishes before creating any tiles. tunerService.addTunable(this, TILES_SETTING); // AutoTileManager can modify mTiles so make sure mTiles has already been initialized. - mAutoTiles = autoTiles.get(); + if (!mFeatureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)) { + mAutoTiles = autoTiles.get(); + } }); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSAutoAddLog.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSAutoAddLog.kt new file mode 100644 index 0000000000000..91cb5bb60e132 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSAutoAddLog.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.qs.pipeline.dagger + +import javax.inject.Qualifier + +/** A [LogBuffer] for the QS pipeline to track auto-added tiles */ +@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class QSAutoAddLog diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSAutoAddModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSAutoAddModule.kt index 99792286d962e..a010ac46a5536 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSAutoAddModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSAutoAddModule.kt @@ -16,13 +16,40 @@ package com.android.systemui.qs.pipeline.dagger +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.repository.AutoAddRepository import com.android.systemui.qs.pipeline.data.repository.AutoAddSettingRepository +import com.android.systemui.qs.pipeline.domain.model.AutoAddable +import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger import dagger.Binds import dagger.Module +import dagger.Provides +import dagger.multibindings.Multibinds -@Module +@Module( + includes = + [ + BaseAutoAddableModule::class, + ] +) abstract class QSAutoAddModule { @Binds abstract fun bindAutoAddRepository(impl: AutoAddSettingRepository): AutoAddRepository + + @Multibinds abstract fun providesAutoAddableSet(): Set + + companion object { + /** + * Provides a logging buffer for all logs related to the new Quick Settings pipeline to log + * auto added tiles. + */ + @Provides + @SysUISingleton + @QSAutoAddLog + fun provideQSAutoAddLogBuffer(factory: LogBufferFactory): LogBuffer { + return factory.create(QSPipelineLogger.AUTO_ADD_TAG, maxSize = 100, systrace = false) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSTileListLog.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSTileListLog.kt index ad8bfeabc6764..c56ca8c27a1f3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSTileListLog.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSTileListLog.kt @@ -19,5 +19,5 @@ import java.lang.annotation.Retention import java.lang.annotation.RetentionPolicy import javax.inject.Qualifier -/** A {@link LogBuffer} for the new QS Pipeline for logging changes to the set of current tiles. */ +/** A [LogBuffer] for the new QS Pipeline for logging changes to the set of current tiles. */ @Qualifier @MustBeDocumented @Retention(RetentionPolicy.RUNTIME) annotation class QSTileListLog 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 new file mode 100644 index 0000000000000..b74739322fcd2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt @@ -0,0 +1,123 @@ +/* + * 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.Dumpable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dump.DumpManager +import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository +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 +import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger +import com.android.systemui.util.asIndenting +import com.android.systemui.util.indentIfPossible +import java.io.PrintWriter +import java.util.concurrent.atomic.AtomicBoolean +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.launch + +/** + * Collects the signals coming from all registered [AutoAddable] and adds/removes tiles accordingly. + */ +@SysUISingleton +class AutoAddInteractor +@Inject +constructor( + private val autoAddables: Set<@JvmSuppressWildcards AutoAddable>, + private val repository: AutoAddRepository, + private val dumpManager: DumpManager, + private val qsPipelineLogger: QSPipelineLogger, + @Application private val scope: CoroutineScope, +) : Dumpable { + + private val initialized = AtomicBoolean(false) + + /** Start collection of signals following the user from [currentTilesInteractor]. */ + fun init(currentTilesInteractor: CurrentTilesInteractor) { + if (!initialized.compareAndSet(false, true)) { + return + } + + dumpManager.registerNormalDumpable(TAG, this) + + scope.launch { + currentTilesInteractor.userId.collectLatest { userId -> + coroutineScope { + val previouslyAdded = repository.autoAddedTiles(userId).stateIn(this) + + autoAddables + .map { addable -> + val autoAddSignal = addable.autoAddSignal(userId) + when (val lifecycle = addable.autoAddTracking) { + is AutoAddTracking.Always -> autoAddSignal + is AutoAddTracking.Disabled -> emptyFlow() + is AutoAddTracking.IfNotAdded -> { + if (lifecycle.spec !in previouslyAdded.value) { + autoAddSignal.filterIsInstance().take(1) + } else { + emptyFlow() + } + } + } + } + .merge() + .collect { signal -> + when (signal) { + is AutoAddSignal.Add -> { + if (signal.spec !in previouslyAdded.value) { + currentTilesInteractor.addTile(signal.spec, signal.position) + qsPipelineLogger.logTileAutoAdded( + userId, + signal.spec, + signal.position + ) + repository.markTileAdded(userId, signal.spec) + } + } + is AutoAddSignal.Remove -> { + currentTilesInteractor.removeTiles(setOf(signal.spec)) + qsPipelineLogger.logTileAutoRemoved(userId, signal.spec) + repository.unmarkTileAdded(userId, signal.spec) + } + } + } + } + } + } + } + + override fun dump(pw: PrintWriter, args: Array) { + with(pw.asIndenting()) { + println("AutoAddables:") + indentIfPossible { autoAddables.forEach { println(it.description) } } + } + } + + companion object { + private const val TAG = "AutoAddInteractor" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt index ff881f767b877..81b682a4836c1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt @@ -125,6 +125,7 @@ interface CurrentTilesInteractor : ProtoDumpable { class CurrentTilesInteractorImpl @Inject constructor( + autoAddInteractor: AutoAddInteractor, private val tileSpecRepository: TileSpecRepository, private val installedTilesComponentRepository: InstalledTilesComponentRepository, private val userRepository: UserRepository, @@ -172,6 +173,10 @@ constructor( if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) { startTileCollection() } + + if (featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)) { + autoAddInteractor.init(this) + } } @OptIn(ExperimentalCoroutinesApi::class) 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 8318ec99e530d..d400faa3091e4 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 @@ -19,6 +19,7 @@ package com.android.systemui.qs.pipeline.shared.logging import android.annotation.UserIdInt import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel +import com.android.systemui.qs.pipeline.dagger.QSAutoAddLog import com.android.systemui.qs.pipeline.dagger.QSTileListLog import com.android.systemui.qs.pipeline.shared.TileSpec import javax.inject.Inject @@ -32,10 +33,12 @@ class QSPipelineLogger @Inject constructor( @QSTileListLog private val tileListLogBuffer: LogBuffer, + @QSAutoAddLog private val tileAutoAddLogBuffer: LogBuffer, ) { companion object { const val TILE_LIST_TAG = "QSTileListLog" + const val AUTO_ADD_TAG = "QSAutoAddableLog" } /** @@ -136,6 +139,31 @@ constructor( ) } + fun logTileAutoAdded(userId: Int, spec: TileSpec, position: Int) { + tileAutoAddLogBuffer.log( + AUTO_ADD_TAG, + LogLevel.DEBUG, + { + int1 = userId + int2 = position + str1 = spec.toString() + }, + { "Tile $str1 auto added for user $int1 at position $int2" } + ) + } + + fun logTileAutoRemoved(userId: Int, spec: TileSpec) { + tileAutoAddLogBuffer.log( + AUTO_ADD_TAG, + LogLevel.DEBUG, + { + int1 = userId + str1 = spec.toString() + }, + { "Tile $str1 auto removed for user $int1" } + ) + } + /** Reasons for destroying an existing tile. */ enum class TileDestroyedReason(val readable: String) { TILE_REMOVED("Tile removed from current set"), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index d98bcee1e01ad..cc42f5afe21e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -140,6 +140,7 @@ public class QSTileHostTest extends SysuiTestCase { mFeatureFlags = new FakeFeatureFlags(); mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false); + mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false); mMainExecutor = new FakeExecutor(new FakeSystemClock()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt new file mode 100644 index 0000000000000..f924b35d9c9c6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt @@ -0,0 +1,201 @@ +/* + * 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.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.dump.DumpManager +import com.android.systemui.qs.pipeline.data.repository.FakeAutoAddRepository +import com.android.systemui.qs.pipeline.domain.autoaddable.FakeAutoAddable +import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking +import com.android.systemui.qs.pipeline.domain.model.AutoAddable +import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +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 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito.inOrder +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@OptIn(ExperimentalCoroutinesApi::class) +class AutoAddInteractorTest : SysuiTestCase() { + private val testScope = TestScope() + + private val autoAddRepository = FakeAutoAddRepository() + + @Mock private lateinit var dumpManager: DumpManager + @Mock private lateinit var currentTilesInteractor: CurrentTilesInteractor + @Mock private lateinit var logger: QSPipelineLogger + private lateinit var underTest: AutoAddInteractor + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(currentTilesInteractor.userId).thenReturn(MutableStateFlow(USER)) + } + + @Test + fun autoAddable_alwaysTrack_addSignal_tileAddedAndMarked() = + testScope.runTest { + val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.Always) + val autoAddedTiles by collectLastValue(autoAddRepository.autoAddedTiles(USER)) + + underTest = createInteractor(setOf(fakeAutoAddable)) + + val position = 3 + fakeAutoAddable.sendAddSignal(USER, position) + runCurrent() + + verify(currentTilesInteractor).addTile(SPEC, position) + assertThat(autoAddedTiles).contains(SPEC) + } + + @Test + fun autoAddable_alwaysTrack_addThenRemoveSignal_tileAddedAndRemoved() = + testScope.runTest { + val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.Always) + val autoAddedTiles by collectLastValue(autoAddRepository.autoAddedTiles(USER)) + + underTest = createInteractor(setOf(fakeAutoAddable)) + + val position = 3 + fakeAutoAddable.sendAddSignal(USER, position) + runCurrent() + fakeAutoAddable.sendRemoveSignal(USER) + runCurrent() + + val inOrder = inOrder(currentTilesInteractor) + inOrder.verify(currentTilesInteractor).addTile(SPEC, position) + inOrder.verify(currentTilesInteractor).removeTiles(setOf(SPEC)) + assertThat(autoAddedTiles).doesNotContain(SPEC) + } + + @Test + fun autoAddable_alwaysTrack_addSignalWhenAddedPreviously_noop() = + testScope.runTest { + val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.Always) + autoAddRepository.markTileAdded(USER, SPEC) + runCurrent() + + underTest = createInteractor(setOf(fakeAutoAddable)) + + val position = 3 + fakeAutoAddable.sendAddSignal(USER, position) + runCurrent() + + verify(currentTilesInteractor, never()).addTile(SPEC, position) + } + + @Test + fun autoAddable_disabled_noInteractionsWithCurrentTilesInteractor() = + testScope.runTest { + val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.Disabled) + val autoAddedTiles by collectLastValue(autoAddRepository.autoAddedTiles(USER)) + + underTest = createInteractor(setOf(fakeAutoAddable)) + + val position = 3 + fakeAutoAddable.sendAddSignal(USER, position) + runCurrent() + fakeAutoAddable.sendRemoveSignal(USER) + runCurrent() + + verify(currentTilesInteractor, never()).addTile(any(), anyInt()) + verify(currentTilesInteractor, never()).removeTiles(any()) + assertThat(autoAddedTiles).doesNotContain(SPEC) + } + + @Test + fun autoAddable_trackIfNotAdded_removeSignal_noop() = + testScope.runTest { + val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.IfNotAdded(SPEC)) + runCurrent() + + underTest = createInteractor(setOf(fakeAutoAddable)) + + fakeAutoAddable.sendRemoveSignal(USER) + runCurrent() + + verify(currentTilesInteractor, never()).addTile(any(), anyInt()) + verify(currentTilesInteractor, never()).removeTiles(any()) + } + + @Test + fun autoAddable_trackIfNotAdded_addSignalWhenPreviouslyAdded_noop() = + testScope.runTest { + val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.IfNotAdded(SPEC)) + autoAddRepository.markTileAdded(USER, SPEC) + runCurrent() + + underTest = createInteractor(setOf(fakeAutoAddable)) + + fakeAutoAddable.sendAddSignal(USER) + runCurrent() + + verify(currentTilesInteractor, never()).addTile(any(), anyInt()) + verify(currentTilesInteractor, never()).removeTiles(any()) + } + + @Test + fun autoAddable_trackIfNotAdded_addSignal_addedAndMarked() = + testScope.runTest { + val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.IfNotAdded(SPEC)) + val autoAddedTiles by collectLastValue(autoAddRepository.autoAddedTiles(USER)) + + underTest = createInteractor(setOf(fakeAutoAddable)) + + val position = 3 + fakeAutoAddable.sendAddSignal(USER, position) + runCurrent() + + verify(currentTilesInteractor).addTile(SPEC, position) + assertThat(autoAddedTiles).contains(SPEC) + } + + private fun createInteractor(autoAddables: Set): AutoAddInteractor { + return AutoAddInteractor( + autoAddables, + autoAddRepository, + dumpManager, + logger, + testScope.backgroundScope + ) + .apply { init(currentTilesInteractor) } + } + + companion object { + private val SPEC = TileSpec.create("spec") + private val USER = 10 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index e7ad4896810bd..522ffc1d498ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -83,6 +83,8 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { @Mock private lateinit var customTileStatePersister: CustomTileStatePersister + @Mock private lateinit var autoAddInteractor: AutoAddInteractor + @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var logger: QSPipelineLogger @@ -100,6 +102,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) featureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true) + featureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, true) userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1)) @@ -107,6 +110,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { underTest = CurrentTilesInteractorImpl( + autoAddInteractor = autoAddInteractor, tileSpecRepository = tileSpecRepository, installedTilesComponentRepository = installedTilesPackageRepository, userRepository = userRepository, @@ -647,6 +651,11 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { assertThat(tiles!![1].spec).isEqualTo(CUSTOM_TILE_SPEC) } + @Test + fun autoAddInteractor_initted() { + verify(autoAddInteractor).init(underTest) + } + private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) { this.state = state this.label = label diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt new file mode 100644 index 0000000000000..9ea079fc9c4b2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt @@ -0,0 +1,42 @@ +/* + * 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.qs.pipeline.shared.TileSpec +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeAutoAddRepository : AutoAddRepository { + + private val autoAddedTilesPerUser = mutableMapOf>>() + + override fun autoAddedTiles(userId: Int): Flow> { + return getFlow(userId) + } + + override suspend fun markTileAdded(userId: Int, spec: TileSpec) { + if (spec == TileSpec.Invalid) return + with(getFlow(userId)) { value = value.toMutableSet().apply { add(spec) } } + } + + override suspend fun unmarkTileAdded(userId: Int, spec: TileSpec) { + with(getFlow(userId)) { value = value.toMutableSet().apply { remove(spec) } } + } + + private fun getFlow(userId: Int): MutableStateFlow> = + autoAddedTilesPerUser.getOrPut(userId) { MutableStateFlow(emptySet()) } +}