Merge changes from topic "278577795" into udc-qpr-dev
* changes: Use a CoreStartable to start AutoAddInteractor Add a AutoAddInteractor Add AutoAddables
This commit is contained in:
committed by
Android (Google) Code Review
commit
4c79ccb1ae
@@ -161,7 +161,9 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P
|
|||||||
// finishes before creating any tiles.
|
// finishes before creating any tiles.
|
||||||
tunerService.addTunable(this, TILES_SETTING);
|
tunerService.addTunable(this, TILES_SETTING);
|
||||||
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
|
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
|
||||||
|
if (!mFeatureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)) {
|
||||||
mAutoTiles = autoTiles.get();
|
mAutoTiles = autoTiles.get();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.content.res.Resources
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.AutoAddableSetting
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.AutoAddableSettingList
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.CastAutoAddable
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.DataSaverAutoAddable
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.DeviceControlsAutoAddable
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.HotspotAutoAddable
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.NightDisplayAutoAddable
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.ReduceBrightColorsAutoAddable
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.WalletAutoAddable
|
||||||
|
import com.android.systemui.qs.pipeline.domain.autoaddable.WorkTileAutoAddable
|
||||||
|
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.multibindings.ElementsIntoSet
|
||||||
|
import dagger.multibindings.IntoSet
|
||||||
|
|
||||||
|
@Module
|
||||||
|
interface BaseAutoAddableModule {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Provides
|
||||||
|
@ElementsIntoSet
|
||||||
|
fun providesAutoAddableSetting(
|
||||||
|
@Main resources: Resources,
|
||||||
|
autoAddableSettingFactory: AutoAddableSetting.Factory
|
||||||
|
): Set<AutoAddable> {
|
||||||
|
return AutoAddableSettingList.parseSettingsResource(
|
||||||
|
resources,
|
||||||
|
autoAddableSettingFactory
|
||||||
|
)
|
||||||
|
.toSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Binds @IntoSet fun bindCastAutoAddable(impl: CastAutoAddable): AutoAddable
|
||||||
|
|
||||||
|
@Binds @IntoSet fun bindDataSaverAutoAddable(impl: DataSaverAutoAddable): AutoAddable
|
||||||
|
|
||||||
|
@Binds @IntoSet fun bindDeviceControlsAutoAddable(impl: DeviceControlsAutoAddable): AutoAddable
|
||||||
|
|
||||||
|
@Binds @IntoSet fun bindHotspotAutoAddable(impl: HotspotAutoAddable): AutoAddable
|
||||||
|
|
||||||
|
@Binds @IntoSet fun bindNightDisplayAutoAddable(impl: NightDisplayAutoAddable): AutoAddable
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoSet
|
||||||
|
fun bindReduceBrightColorsAutoAddable(impl: ReduceBrightColorsAutoAddable): AutoAddable
|
||||||
|
|
||||||
|
@Binds @IntoSet fun bindWalletAutoAddable(impl: WalletAutoAddable): AutoAddable
|
||||||
|
|
||||||
|
@Binds @IntoSet fun bindWorkModeAutoAddable(impl: WorkTileAutoAddable): AutoAddable
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -16,13 +16,40 @@
|
|||||||
|
|
||||||
package com.android.systemui.qs.pipeline.dagger
|
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.AutoAddRepository
|
||||||
import com.android.systemui.qs.pipeline.data.repository.AutoAddSettingRepository
|
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.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.multibindings.Multibinds
|
||||||
|
|
||||||
@Module
|
@Module(
|
||||||
|
includes =
|
||||||
|
[
|
||||||
|
BaseAutoAddableModule::class,
|
||||||
|
]
|
||||||
|
)
|
||||||
abstract class QSAutoAddModule {
|
abstract class QSAutoAddModule {
|
||||||
|
|
||||||
@Binds abstract fun bindAutoAddRepository(impl: AutoAddSettingRepository): AutoAddRepository
|
@Binds abstract fun bindAutoAddRepository(impl: AutoAddSettingRepository): AutoAddRepository
|
||||||
|
|
||||||
|
@Multibinds abstract fun providesAutoAddableSet(): Set<AutoAddable>
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
|
|||||||
import com.android.systemui.qs.pipeline.data.repository.TileSpecSettingsRepository
|
import com.android.systemui.qs.pipeline.data.repository.TileSpecSettingsRepository
|
||||||
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
|
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
|
||||||
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractorImpl
|
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractorImpl
|
||||||
import com.android.systemui.qs.pipeline.prototyping.PrototypeCoreStartable
|
import com.android.systemui.qs.pipeline.domain.startable.QSPipelineCoreStartable
|
||||||
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
|
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
@@ -53,8 +53,8 @@ abstract class QSPipelineModule {
|
|||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@ClassKey(PrototypeCoreStartable::class)
|
@ClassKey(QSPipelineCoreStartable::class)
|
||||||
abstract fun providePrototypeCoreStartable(startable: PrototypeCoreStartable): CoreStartable
|
abstract fun provideCoreStartable(startable: QSPipelineCoreStartable): CoreStartable
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,5 +19,5 @@ import java.lang.annotation.Retention
|
|||||||
import java.lang.annotation.RetentionPolicy
|
import java.lang.annotation.RetentionPolicy
|
||||||
import javax.inject.Qualifier
|
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
|
@Qualifier @MustBeDocumented @Retention(RetentionPolicy.RUNTIME) annotation class QSTileListLog
|
||||||
|
|||||||
@@ -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.domain.autoaddable
|
||||||
|
|
||||||
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
|
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.TileSpec
|
||||||
|
import com.android.systemui.util.settings.SecureSettings
|
||||||
|
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import java.util.Objects
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It tracks a specific `Secure` int [setting] and when its value changes to non-zero, it will emit
|
||||||
|
* a [AutoAddSignal.Add] for [spec].
|
||||||
|
*/
|
||||||
|
class AutoAddableSetting
|
||||||
|
@AssistedInject
|
||||||
|
constructor(
|
||||||
|
private val secureSettings: SecureSettings,
|
||||||
|
@Background private val bgDispatcher: CoroutineDispatcher,
|
||||||
|
@Assisted private val setting: String,
|
||||||
|
@Assisted private val spec: TileSpec,
|
||||||
|
) : AutoAddable {
|
||||||
|
|
||||||
|
override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
|
||||||
|
return secureSettings
|
||||||
|
.observerFlow(userId, setting)
|
||||||
|
.onStart { emit(Unit) }
|
||||||
|
.map { secureSettings.getIntForUser(setting, 0, userId) != 0 }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.filter { it }
|
||||||
|
.map { AutoAddSignal.Add(spec) }
|
||||||
|
.flowOn(bgDispatcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val autoAddTracking = AutoAddTracking.IfNotAdded(spec)
|
||||||
|
|
||||||
|
override val description = "AutoAddableSetting: $setting:$spec ($autoAddTracking)"
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is AutoAddableSetting && spec == other.spec && setting == other.setting
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return Objects.hash(spec, setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return description
|
||||||
|
}
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(setting: String, spec: TileSpec): AutoAddableSetting
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.content.res.Resources
|
||||||
|
import android.util.Log
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
|
||||||
|
object AutoAddableSettingList {
|
||||||
|
|
||||||
|
/** Parses [R.array.config_quickSettingsAutoAdd] into a collection of [AutoAddableSetting]. */
|
||||||
|
fun parseSettingsResource(
|
||||||
|
resources: Resources,
|
||||||
|
autoAddableSettingFactory: AutoAddableSetting.Factory,
|
||||||
|
): Iterable<AutoAddable> {
|
||||||
|
val autoAddList = resources.getStringArray(R.array.config_quickSettingsAutoAdd)
|
||||||
|
return autoAddList.mapNotNull {
|
||||||
|
val elements = it.split(SETTING_SEPARATOR, limit = 2)
|
||||||
|
if (elements.size == 2) {
|
||||||
|
val setting = elements[0]
|
||||||
|
val spec = elements[1]
|
||||||
|
val tileSpec = TileSpec.create(spec)
|
||||||
|
if (tileSpec == TileSpec.Invalid) {
|
||||||
|
Log.w(TAG, "Malformed item in array: $it")
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
autoAddableSettingFactory.create(setting, TileSpec.create(spec))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Malformed item in array: $it")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val SETTING_SEPARATOR = ":"
|
||||||
|
private const val TAG = "AutoAddableSettingList"
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||||
|
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.TileSpec
|
||||||
|
import com.android.systemui.statusbar.policy.CallbackController
|
||||||
|
import kotlinx.coroutines.channels.ProducerScope
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/** Generic [AutoAddable] for tiles that are added based on a signal from a [CallbackController]. */
|
||||||
|
abstract class CallbackControllerAutoAddable<
|
||||||
|
Callback : Any, Controller : CallbackController<Callback>>(
|
||||||
|
private val controller: Controller,
|
||||||
|
) : AutoAddable {
|
||||||
|
|
||||||
|
/** [TileSpec] for the tile to add. */
|
||||||
|
protected abstract val spec: TileSpec
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to be used to determine when to add the tile. When the callback determines that the
|
||||||
|
* feature has been enabled, it should call [sendAdd].
|
||||||
|
*/
|
||||||
|
protected abstract fun ProducerScope<AutoAddSignal>.getCallback(): Callback
|
||||||
|
|
||||||
|
/** Sends an [AutoAddSignal.Add] for [spec]. */
|
||||||
|
protected fun ProducerScope<AutoAddSignal>.sendAdd() {
|
||||||
|
trySend(AutoAddSignal.Add(spec))
|
||||||
|
}
|
||||||
|
|
||||||
|
final override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
|
||||||
|
return conflatedCallbackFlow {
|
||||||
|
val callback = getCallback()
|
||||||
|
controller.addCallback(callback)
|
||||||
|
|
||||||
|
awaitClose { controller.removeCallback(callback) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val autoAddTracking: AutoAddTracking
|
||||||
|
get() = AutoAddTracking.IfNotAdded(spec)
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.CastTile
|
||||||
|
import com.android.systemui.statusbar.policy.CastController
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.channels.ProducerScope
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AutoAddable] for [CastTile.TILE_SPEC].
|
||||||
|
*
|
||||||
|
* It will send a signal to add the tile when there's a casting device connected or connecting.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class CastAutoAddable
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val controller: CastController,
|
||||||
|
) : CallbackControllerAutoAddable<CastController.Callback, CastController>(controller) {
|
||||||
|
|
||||||
|
override val spec: TileSpec
|
||||||
|
get() = TileSpec.create(CastTile.TILE_SPEC)
|
||||||
|
|
||||||
|
override fun ProducerScope<AutoAddSignal>.getCallback(): CastController.Callback {
|
||||||
|
return CastController.Callback {
|
||||||
|
val isCasting =
|
||||||
|
controller.castDevices.any {
|
||||||
|
it.state == CastController.CastDevice.STATE_CONNECTED ||
|
||||||
|
it.state == CastController.CastDevice.STATE_CONNECTING
|
||||||
|
}
|
||||||
|
if (isCasting) {
|
||||||
|
sendAdd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val description = "CastAutoAddable ($autoAddTracking)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.DataSaverTile
|
||||||
|
import com.android.systemui.statusbar.policy.DataSaverController
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.channels.ProducerScope
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AutoAddable] for [DataSaverTile.TILE_SPEC].
|
||||||
|
*
|
||||||
|
* It will send a signal to add the tile when data saver is enabled.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class DataSaverAutoAddable
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
dataSaverController: DataSaverController,
|
||||||
|
) :
|
||||||
|
CallbackControllerAutoAddable<DataSaverController.Listener, DataSaverController>(
|
||||||
|
dataSaverController
|
||||||
|
) {
|
||||||
|
|
||||||
|
override val spec
|
||||||
|
get() = TileSpec.create(DataSaverTile.TILE_SPEC)
|
||||||
|
|
||||||
|
override fun ProducerScope<AutoAddSignal>.getCallback(): DataSaverController.Listener {
|
||||||
|
return DataSaverController.Listener { enabled ->
|
||||||
|
if (enabled) {
|
||||||
|
sendAdd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val description = "DataSaverAutoAddable ($autoAddTracking)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
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.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.DeviceControlsTile
|
||||||
|
import com.android.systemui.statusbar.policy.DeviceControlsController
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AutoAddable] for [DeviceControlsTile.TILE_SPEC].
|
||||||
|
*
|
||||||
|
* It will send a signal to add the tile when updating to a device that supports device controls. It
|
||||||
|
* will send a signal to remove the tile when the device does not support controls.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class DeviceControlsAutoAddable
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val deviceControlsController: DeviceControlsController,
|
||||||
|
) : AutoAddable {
|
||||||
|
|
||||||
|
private val spec = TileSpec.create(DeviceControlsTile.TILE_SPEC)
|
||||||
|
|
||||||
|
override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
|
||||||
|
return conflatedCallbackFlow {
|
||||||
|
val callback =
|
||||||
|
object : DeviceControlsController.Callback {
|
||||||
|
override fun onControlsUpdate(position: Int?) {
|
||||||
|
position?.let { trySend(AutoAddSignal.Add(spec, position)) }
|
||||||
|
deviceControlsController.removeCallback()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeControlsAutoTracker() {
|
||||||
|
trySend(AutoAddSignal.Remove(spec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceControlsController.setCallback(callback)
|
||||||
|
|
||||||
|
awaitClose { deviceControlsController.removeCallback() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val autoAddTracking: AutoAddTracking
|
||||||
|
get() = AutoAddTracking.Always
|
||||||
|
|
||||||
|
override val description = "DeviceControlsAutoAddable ($autoAddTracking)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* 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.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.HotspotTile
|
||||||
|
import com.android.systemui.statusbar.policy.HotspotController
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.channels.ProducerScope
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AutoAddable] for [HotspotTile.TILE_SPEC].
|
||||||
|
*
|
||||||
|
* It will send a signal to add the tile when hotspot is enabled.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class HotspotAutoAddable
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
hotspotController: HotspotController,
|
||||||
|
) :
|
||||||
|
CallbackControllerAutoAddable<HotspotController.Callback, HotspotController>(
|
||||||
|
hotspotController
|
||||||
|
) {
|
||||||
|
|
||||||
|
override val spec
|
||||||
|
get() = TileSpec.create(HotspotTile.TILE_SPEC)
|
||||||
|
|
||||||
|
override fun ProducerScope<AutoAddSignal>.getCallback(): HotspotController.Callback {
|
||||||
|
return HotspotController.Callback { enabled, _ ->
|
||||||
|
if (enabled) {
|
||||||
|
sendAdd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val description = "HotspotAutoAddable ($autoAddTracking)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.content.Context
|
||||||
|
import android.hardware.display.ColorDisplayManager
|
||||||
|
import android.hardware.display.NightDisplayListener
|
||||||
|
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||||
|
import com.android.systemui.dagger.NightDisplayListenerModule
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
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.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.NightDisplayTile
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AutoAddable] for [NightDisplayTile.TILE_SPEC].
|
||||||
|
*
|
||||||
|
* It will send a signal to add the tile when night display is enabled or when the auto mode changes
|
||||||
|
* to one that supports night display.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class NightDisplayAutoAddable
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val nightDisplayListenerBuilder: NightDisplayListenerModule.Builder,
|
||||||
|
context: Context,
|
||||||
|
) : AutoAddable {
|
||||||
|
|
||||||
|
private val enabled = ColorDisplayManager.isNightDisplayAvailable(context)
|
||||||
|
private val spec = TileSpec.create(NightDisplayTile.TILE_SPEC)
|
||||||
|
|
||||||
|
override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
|
||||||
|
return conflatedCallbackFlow {
|
||||||
|
val nightDisplayListener = nightDisplayListenerBuilder.setUser(userId).build()
|
||||||
|
|
||||||
|
val callback =
|
||||||
|
object : NightDisplayListener.Callback {
|
||||||
|
override fun onActivated(activated: Boolean) {
|
||||||
|
if (activated) {
|
||||||
|
sendAdd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAutoModeChanged(autoMode: Int) {
|
||||||
|
if (
|
||||||
|
autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME ||
|
||||||
|
autoMode == ColorDisplayManager.AUTO_MODE_TWILIGHT
|
||||||
|
) {
|
||||||
|
sendAdd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendAdd() {
|
||||||
|
trySend(AutoAddSignal.Add(spec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nightDisplayListener.setCallback(callback)
|
||||||
|
|
||||||
|
awaitClose { nightDisplayListener.setCallback(null) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val autoAddTracking =
|
||||||
|
if (enabled) {
|
||||||
|
AutoAddTracking.IfNotAdded(spec)
|
||||||
|
} else {
|
||||||
|
AutoAddTracking.Disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
override val description = "NightDisplayAutoAddable ($autoAddTracking)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.qs.ReduceBrightColorsController
|
||||||
|
import com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE
|
||||||
|
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
|
||||||
|
import com.android.systemui.qs.tiles.ReduceBrightColorsTile
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Named
|
||||||
|
import kotlinx.coroutines.channels.ProducerScope
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AutoAddable] for [ReduceBrightColorsTile.TILE_SPEC].
|
||||||
|
*
|
||||||
|
* It will send a signal to add the tile when reduce bright colors is enabled.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class ReduceBrightColorsAutoAddable
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
controller: ReduceBrightColorsController,
|
||||||
|
@Named(RBC_AVAILABLE) private val available: Boolean,
|
||||||
|
) :
|
||||||
|
CallbackControllerAutoAddable<
|
||||||
|
ReduceBrightColorsController.Listener, ReduceBrightColorsController
|
||||||
|
>(controller) {
|
||||||
|
|
||||||
|
override val spec: TileSpec
|
||||||
|
get() = TileSpec.create(ReduceBrightColorsTile.TILE_SPEC)
|
||||||
|
|
||||||
|
override fun ProducerScope<AutoAddSignal>.getCallback(): ReduceBrightColorsController.Listener {
|
||||||
|
return object : ReduceBrightColorsController.Listener {
|
||||||
|
override fun onActivated(activated: Boolean) {
|
||||||
|
if (activated) {
|
||||||
|
sendAdd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val autoAddTracking
|
||||||
|
get() =
|
||||||
|
if (available) {
|
||||||
|
super.autoAddTracking
|
||||||
|
} else {
|
||||||
|
AutoAddTracking.Disabled
|
||||||
|
}
|
||||||
|
|
||||||
|
override val description = "ReduceBrightColorsAutoAddable ($autoAddTracking)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.content.ComponentName
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.text.TextUtils
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
|
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.TileSpec
|
||||||
|
import com.android.systemui.statusbar.policy.SafetyController
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AutoAddable] for the safety tile.
|
||||||
|
*
|
||||||
|
* It will send a signal to add the tile when the feature is enabled, indicating the component
|
||||||
|
* corresponding to the tile. If the feature is disabled, it will send a signal to remove the tile.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class SafetyCenterAutoAddable
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val safetyController: SafetyController,
|
||||||
|
private val packageManager: PackageManager,
|
||||||
|
@Main private val resources: Resources,
|
||||||
|
@Background private val bgDispatcher: CoroutineDispatcher,
|
||||||
|
) : AutoAddable {
|
||||||
|
|
||||||
|
private suspend fun getSpec(): TileSpec? {
|
||||||
|
val specClass = resources.getString(R.string.safety_quick_settings_tile_class)
|
||||||
|
return if (TextUtils.isEmpty(specClass)) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
val packageName =
|
||||||
|
withContext(bgDispatcher) { packageManager.permissionControllerPackageName }
|
||||||
|
TileSpec.create(ComponentName(packageName, specClass))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
|
||||||
|
return conflatedCallbackFlow {
|
||||||
|
val spec = getSpec()
|
||||||
|
if (spec != null) {
|
||||||
|
// If not added, we always try to add it
|
||||||
|
trySend(AutoAddSignal.Add(spec))
|
||||||
|
val listener =
|
||||||
|
SafetyController.Listener { isSafetyCenterEnabled ->
|
||||||
|
if (isSafetyCenterEnabled) {
|
||||||
|
trySend(AutoAddSignal.Add(spec))
|
||||||
|
} else {
|
||||||
|
trySend(AutoAddSignal.Remove(spec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
safetyController.addCallback(listener)
|
||||||
|
|
||||||
|
awaitClose { safetyController.removeCallback(listener) }
|
||||||
|
} else {
|
||||||
|
awaitClose {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val autoAddTracking: AutoAddTracking
|
||||||
|
get() = AutoAddTracking.Always
|
||||||
|
|
||||||
|
override val description = "SafetyCenterAutoAddable ($autoAddTracking)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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.dagger.SysUISingleton
|
||||||
|
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.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.QuickAccessWalletTile
|
||||||
|
import com.android.systemui.statusbar.policy.WalletController
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AutoAddable] for [QuickAccessWalletTile.TILE_SPEC].
|
||||||
|
*
|
||||||
|
* It will always try to add the tile if [WalletController.getWalletPosition] is non-null.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class WalletAutoAddable
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val walletController: WalletController,
|
||||||
|
) : AutoAddable {
|
||||||
|
|
||||||
|
private val spec = TileSpec.create(QuickAccessWalletTile.TILE_SPEC)
|
||||||
|
|
||||||
|
override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
|
||||||
|
return flow {
|
||||||
|
val position = walletController.getWalletPosition()
|
||||||
|
if (position != null) {
|
||||||
|
emit(AutoAddSignal.Add(spec, position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val autoAddTracking: AutoAddTracking
|
||||||
|
get() = AutoAddTracking.IfNotAdded(spec)
|
||||||
|
|
||||||
|
override val description = "WalletAutoAddable ($autoAddTracking)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.content.pm.UserInfo
|
||||||
|
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
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.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.WorkModeTile
|
||||||
|
import com.android.systemui.settings.UserTracker
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [AutoAddable] for [WorkModeTile.TILE_SPEC].
|
||||||
|
*
|
||||||
|
* It will send a signal to add the tile when there is a managed profile for the current user, and a
|
||||||
|
* signal to remove it if there is not.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class WorkTileAutoAddable @Inject constructor(private val userTracker: UserTracker) : AutoAddable {
|
||||||
|
|
||||||
|
private val spec = TileSpec.create(WorkModeTile.TILE_SPEC)
|
||||||
|
|
||||||
|
override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
|
||||||
|
return 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))
|
||||||
|
} else {
|
||||||
|
trySend(AutoAddSignal.Remove(spec))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val callback =
|
||||||
|
object : UserTracker.Callback {
|
||||||
|
override fun onProfilesChanged(profiles: List<UserInfo>) {
|
||||||
|
maybeSend(profiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
userTracker.addCallback(callback) { it.run() }
|
||||||
|
maybeSend(userTracker.userProfiles)
|
||||||
|
|
||||||
|
awaitClose { userTracker.removeCallback(callback) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val autoAddTracking = AutoAddTracking.Always
|
||||||
|
|
||||||
|
override val description = "WorkTileAutoAddable ($autoAddTracking)"
|
||||||
|
}
|
||||||
@@ -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<AutoAddSignal.Add>().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<out String>) {
|
||||||
|
with(pw.asIndenting()) {
|
||||||
|
println("AutoAddables:")
|
||||||
|
indentIfPossible { autoAddables.forEach { println(it.description) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "AutoAddInteractor"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.model
|
||||||
|
|
||||||
|
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
|
||||||
|
/** Signal indicating when a tile needs to be auto-added or removed */
|
||||||
|
sealed interface AutoAddSignal {
|
||||||
|
/** Tile for this object */
|
||||||
|
val spec: TileSpec
|
||||||
|
|
||||||
|
/** Signal for auto-adding a tile at [position]. */
|
||||||
|
data class Add(
|
||||||
|
override val spec: TileSpec,
|
||||||
|
val position: Int = POSITION_AT_END,
|
||||||
|
) : AutoAddSignal
|
||||||
|
|
||||||
|
/** Signal for removing a tile. */
|
||||||
|
data class Remove(
|
||||||
|
override val spec: TileSpec,
|
||||||
|
) : AutoAddSignal
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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.model
|
||||||
|
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
|
||||||
|
/** Strategy for when to track a particular [AutoAddable]. */
|
||||||
|
sealed interface AutoAddTracking {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the signals from the associated [AutoAddable] should all be collected and
|
||||||
|
* reacted accordingly. It may have [AutoAddSignal.Add] and [AutoAddSignal.Remove].
|
||||||
|
*/
|
||||||
|
object Always : AutoAddTracking {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "Always"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the associated [AutoAddable] is [Disabled] and doesn't need to be collected.
|
||||||
|
*/
|
||||||
|
object Disabled : AutoAddTracking {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "Disabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only the first [AutoAddSignal.Add] for each flow of signals needs to be collected, and only
|
||||||
|
* if the tile hasn't been auto-added yet. The associated [AutoAddable] will only emit
|
||||||
|
* [AutoAddSignal.Add].
|
||||||
|
*/
|
||||||
|
data class IfNotAdded(val spec: TileSpec) : AutoAddTracking
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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.model
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks conditions for auto-adding or removing specific tiles.
|
||||||
|
*
|
||||||
|
* When creating a new [AutoAddable], it needs to be registered in a [Module] like
|
||||||
|
* [BaseAutoAddableModule], for example:
|
||||||
|
* ```
|
||||||
|
* @Binds
|
||||||
|
* @IntoSet
|
||||||
|
* fun providesMyAutoAddable(autoAddable: MyAutoAddable): AutoAddable
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
interface AutoAddable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals associated with a particular user indicating whether a particular tile needs to be
|
||||||
|
* auto-added or auto-removed.
|
||||||
|
*/
|
||||||
|
fun autoAddSignal(userId: Int): Flow<AutoAddSignal>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lifecycle for this object. It indicates in which cases [autoAddSignal] should be collected
|
||||||
|
*/
|
||||||
|
val autoAddTracking: AutoAddTracking
|
||||||
|
|
||||||
|
/** Human readable description */
|
||||||
|
val description: String
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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.startable
|
||||||
|
|
||||||
|
import com.android.systemui.CoreStartable
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.flags.FeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
|
import com.android.systemui.qs.pipeline.domain.interactor.AutoAddInteractor
|
||||||
|
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@SysUISingleton
|
||||||
|
class QSPipelineCoreStartable
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val currentTilesInteractor: CurrentTilesInteractor,
|
||||||
|
private val autoAddInteractor: AutoAddInteractor,
|
||||||
|
private val featureFlags: FeatureFlags,
|
||||||
|
) : CoreStartable {
|
||||||
|
|
||||||
|
override fun start() {
|
||||||
|
if (
|
||||||
|
featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST) &&
|
||||||
|
featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)
|
||||||
|
) {
|
||||||
|
autoAddInteractor.init(currentTilesInteractor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.prototyping
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.android.systemui.CoreStartable
|
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
|
||||||
import com.android.systemui.dagger.qualifiers.Application
|
|
||||||
import com.android.systemui.flags.FeatureFlags
|
|
||||||
import com.android.systemui.flags.Flags
|
|
||||||
import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository
|
|
||||||
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
|
|
||||||
import com.android.systemui.qs.pipeline.shared.TileSpec
|
|
||||||
import com.android.systemui.statusbar.commandline.Command
|
|
||||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
|
||||||
import com.android.systemui.user.data.repository.UserRepository
|
|
||||||
import java.io.PrintWriter
|
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for observing results while prototyping.
|
|
||||||
*
|
|
||||||
* The flows do their own logging, so we just need to make sure that they collect.
|
|
||||||
*
|
|
||||||
* This will be torn down together with the last of the new pipeline flags remaining here.
|
|
||||||
*/
|
|
||||||
// TODO(b/270385608)
|
|
||||||
@SysUISingleton
|
|
||||||
class PrototypeCoreStartable
|
|
||||||
@Inject
|
|
||||||
constructor(
|
|
||||||
private val tileSpecRepository: TileSpecRepository,
|
|
||||||
private val autoAddRepository: AutoAddRepository,
|
|
||||||
private val userRepository: UserRepository,
|
|
||||||
private val featureFlags: FeatureFlags,
|
|
||||||
@Application private val scope: CoroutineScope,
|
|
||||||
private val commandRegistry: CommandRegistry,
|
|
||||||
) : CoreStartable {
|
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
override fun start() {
|
|
||||||
if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
|
|
||||||
scope.launch {
|
|
||||||
userRepository.selectedUserInfo
|
|
||||||
.flatMapLatest { user -> tileSpecRepository.tilesSpecs(user.id) }
|
|
||||||
.collect {}
|
|
||||||
}
|
|
||||||
if (featureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)) {
|
|
||||||
scope.launch {
|
|
||||||
userRepository.selectedUserInfo
|
|
||||||
.flatMapLatest { user -> autoAddRepository.autoAddedTiles(user.id) }
|
|
||||||
.collect { tiles -> Log.d(TAG, "Auto-added tiles: $tiles") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commandRegistry.registerCommand(COMMAND, ::CommandExecutor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class CommandExecutor : Command {
|
|
||||||
override fun execute(pw: PrintWriter, args: List<String>) {
|
|
||||||
if (args.size < 2) {
|
|
||||||
pw.println("Error: needs at least two arguments")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val spec = TileSpec.create(args[1])
|
|
||||||
if (spec == TileSpec.Invalid) {
|
|
||||||
pw.println("Error: Invalid tile spec ${args[1]}")
|
|
||||||
}
|
|
||||||
if (args[0] == "add") {
|
|
||||||
performAdd(args, spec)
|
|
||||||
pw.println("Requested tile added")
|
|
||||||
} else if (args[0] == "remove") {
|
|
||||||
performRemove(args, spec)
|
|
||||||
pw.println("Requested tile removed")
|
|
||||||
} else {
|
|
||||||
pw.println("Error: unknown command")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun performAdd(args: List<String>, spec: TileSpec) {
|
|
||||||
val position = args.getOrNull(2)?.toInt() ?: TileSpecRepository.POSITION_AT_END
|
|
||||||
val user = args.getOrNull(3)?.toInt() ?: userRepository.getSelectedUserInfo().id
|
|
||||||
scope.launch { tileSpecRepository.addTile(user, spec, position) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun performRemove(args: List<String>, spec: TileSpec) {
|
|
||||||
val user = args.getOrNull(2)?.toInt() ?: userRepository.getSelectedUserInfo().id
|
|
||||||
scope.launch { tileSpecRepository.removeTiles(user, listOf(spec)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun help(pw: PrintWriter) {
|
|
||||||
pw.println("Usage: adb shell cmd statusbar $COMMAND:")
|
|
||||||
pw.println(" add <spec> [position] [user]")
|
|
||||||
pw.println(" remove <spec> [user]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val COMMAND = "qs-pipeline"
|
|
||||||
private const val TAG = "PrototypeCoreStartable"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -52,7 +52,11 @@ sealed class TileSpec private constructor(open val spec: String) {
|
|||||||
internal constructor(
|
internal constructor(
|
||||||
override val spec: String,
|
override val spec: String,
|
||||||
val componentName: ComponentName,
|
val componentName: ComponentName,
|
||||||
) : TileSpec(spec)
|
) : TileSpec(spec) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return "CustomTileSpec(${componentName.toShortString()})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** Create a [TileSpec] from the string [spec]. */
|
/** Create a [TileSpec] from the string [spec]. */
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.systemui.qs.pipeline.shared.logging
|
|||||||
import android.annotation.UserIdInt
|
import android.annotation.UserIdInt
|
||||||
import com.android.systemui.log.LogBuffer
|
import com.android.systemui.log.LogBuffer
|
||||||
import com.android.systemui.log.LogLevel
|
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.dagger.QSTileListLog
|
||||||
import com.android.systemui.qs.pipeline.shared.TileSpec
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -32,10 +33,12 @@ class QSPipelineLogger
|
|||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
@QSTileListLog private val tileListLogBuffer: LogBuffer,
|
@QSTileListLog private val tileListLogBuffer: LogBuffer,
|
||||||
|
@QSAutoAddLog private val tileAutoAddLogBuffer: LogBuffer,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TILE_LIST_TAG = "QSTileListLog"
|
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. */
|
/** Reasons for destroying an existing tile. */
|
||||||
enum class TileDestroyedReason(val readable: String) {
|
enum class TileDestroyedReason(val readable: String) {
|
||||||
TILE_REMOVED("Tile removed from current set"),
|
TILE_REMOVED("Tile removed from current set"),
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
|||||||
mFeatureFlags = new FakeFeatureFlags();
|
mFeatureFlags = new FakeFeatureFlags();
|
||||||
|
|
||||||
mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false);
|
mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false);
|
||||||
|
mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false);
|
||||||
|
|
||||||
mMainExecutor = new FakeExecutor(new FakeSystemClock());
|
mMainExecutor = new FakeExecutor(new FakeSystemClock());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.content.ComponentName
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class AutoAddableSettingListTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private val factory =
|
||||||
|
object : AutoAddableSetting.Factory {
|
||||||
|
override fun create(setting: String, spec: TileSpec): AutoAddableSetting {
|
||||||
|
return AutoAddableSetting(
|
||||||
|
mock(),
|
||||||
|
mock(),
|
||||||
|
setting,
|
||||||
|
spec,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun correctLines_correctAutoAddables() {
|
||||||
|
val setting1 = "setting1"
|
||||||
|
val setting2 = "setting2"
|
||||||
|
val spec1 = TileSpec.create("spec1")
|
||||||
|
val spec2 = TileSpec.create(ComponentName("pkg", "cls"))
|
||||||
|
|
||||||
|
context.orCreateTestableResources.addOverride(
|
||||||
|
R.array.config_quickSettingsAutoAdd,
|
||||||
|
arrayOf(toStringLine(setting1, spec1), toStringLine(setting2, spec2))
|
||||||
|
)
|
||||||
|
|
||||||
|
val autoAddables = AutoAddableSettingList.parseSettingsResource(context.resources, factory)
|
||||||
|
|
||||||
|
assertThat(autoAddables)
|
||||||
|
.containsExactly(factory.create(setting1, spec1), factory.create(setting2, spec2))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun malformedLine_ignored() {
|
||||||
|
val setting = "setting"
|
||||||
|
val spec = TileSpec.create("spec")
|
||||||
|
|
||||||
|
context.orCreateTestableResources.addOverride(
|
||||||
|
R.array.config_quickSettingsAutoAdd,
|
||||||
|
arrayOf(toStringLine(setting, spec), "bad_line")
|
||||||
|
)
|
||||||
|
|
||||||
|
val autoAddables = AutoAddableSettingList.parseSettingsResource(context.resources, factory)
|
||||||
|
|
||||||
|
assertThat(autoAddables).containsExactly(factory.create(setting, spec))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun invalidSpec_ignored() {
|
||||||
|
val setting = "setting"
|
||||||
|
val spec = TileSpec.create("spec")
|
||||||
|
|
||||||
|
context.orCreateTestableResources.addOverride(
|
||||||
|
R.array.config_quickSettingsAutoAdd,
|
||||||
|
arrayOf(toStringLine(setting, spec), "invalid:")
|
||||||
|
)
|
||||||
|
|
||||||
|
val autoAddables = AutoAddableSettingList.parseSettingsResource(context.resources, factory)
|
||||||
|
|
||||||
|
assertThat(autoAddables).containsExactly(factory.create(setting, spec))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun toStringLine(setting: String, spec: TileSpec) =
|
||||||
|
"$setting$SETTINGS_SEPARATOR${spec.spec}"
|
||||||
|
private const val SETTINGS_SEPARATOR = ":"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.testing.AndroidTestingRunner
|
||||||
|
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.qs.pipeline.domain.model.AutoAddSignal
|
||||||
|
import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
import com.android.systemui.util.settings.FakeSettings
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class AutoAddableSettingTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private val testDispatcher = StandardTestDispatcher()
|
||||||
|
private val testScope = TestScope(testDispatcher)
|
||||||
|
|
||||||
|
private val secureSettings = FakeSettings()
|
||||||
|
private val underTest =
|
||||||
|
AutoAddableSetting(
|
||||||
|
secureSettings,
|
||||||
|
testDispatcher,
|
||||||
|
SETTING,
|
||||||
|
SPEC,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun settingNotSet_noSignal() =
|
||||||
|
testScope.runTest {
|
||||||
|
val userId = 0
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(userId))
|
||||||
|
|
||||||
|
assertThat(signal).isNull() // null means no emitted value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun settingSetTo0_noSignal() =
|
||||||
|
testScope.runTest {
|
||||||
|
val userId = 0
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(userId))
|
||||||
|
|
||||||
|
secureSettings.putIntForUser(SETTING, 0, userId)
|
||||||
|
|
||||||
|
assertThat(signal).isNull() // null means no emitted value
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun settingSetToNon0_signal() =
|
||||||
|
testScope.runTest {
|
||||||
|
val userId = 0
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(userId))
|
||||||
|
|
||||||
|
secureSettings.putIntForUser(SETTING, 42, userId)
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun settingSetForUser_onlySignalInThatUser() =
|
||||||
|
testScope.runTest {
|
||||||
|
val signal0 by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
val signal1 by collectLastValue(underTest.autoAddSignal(1))
|
||||||
|
|
||||||
|
secureSettings.putIntForUser(SETTING, /* value */ 42, /* userHandle */ 1)
|
||||||
|
|
||||||
|
assertThat(signal0).isNull()
|
||||||
|
assertThat(signal1).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun multipleNonZeroChanges_onlyOneSignal() =
|
||||||
|
testScope.runTest {
|
||||||
|
val userId = 0
|
||||||
|
val signals by collectValues(underTest.autoAddSignal(userId))
|
||||||
|
|
||||||
|
secureSettings.putIntForUser(SETTING, 1, userId)
|
||||||
|
secureSettings.putIntForUser(SETTING, 2, userId)
|
||||||
|
|
||||||
|
assertThat(signals.size).isEqualTo(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun strategyIfNotAdded() {
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.IfNotAdded(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SETTING = "setting"
|
||||||
|
private val SPEC = TileSpec.create("spec")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
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
|
||||||
|
import com.android.systemui.statusbar.policy.CallbackController
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.channels.ProducerScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.test.runCurrent
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class CallbackControllerAutoAddableTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun callbackAddedAndRemoved() = runTest {
|
||||||
|
val controller = TestableController()
|
||||||
|
val callback = object : TestableController.Callback {}
|
||||||
|
val underTest =
|
||||||
|
object :
|
||||||
|
CallbackControllerAutoAddable<TestableController.Callback, TestableController>(
|
||||||
|
controller
|
||||||
|
) {
|
||||||
|
override val description: String = ""
|
||||||
|
override val spec: TileSpec
|
||||||
|
get() = SPEC
|
||||||
|
|
||||||
|
override fun ProducerScope<AutoAddSignal>.getCallback():
|
||||||
|
TestableController.Callback {
|
||||||
|
return callback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val job = launch { underTest.autoAddSignal(0).collect {} }
|
||||||
|
runCurrent()
|
||||||
|
assertThat(controller.callbacks).containsExactly(callback)
|
||||||
|
job.cancel()
|
||||||
|
runCurrent()
|
||||||
|
assertThat(controller.callbacks).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sendAddFromCallback() = runTest {
|
||||||
|
val controller = TestableController()
|
||||||
|
val underTest =
|
||||||
|
object :
|
||||||
|
CallbackControllerAutoAddable<TestableController.Callback, TestableController>(
|
||||||
|
controller
|
||||||
|
) {
|
||||||
|
override val description: String = ""
|
||||||
|
|
||||||
|
override val spec: TileSpec
|
||||||
|
get() = SPEC
|
||||||
|
|
||||||
|
override fun ProducerScope<AutoAddSignal>.getCallback():
|
||||||
|
TestableController.Callback {
|
||||||
|
return object : TestableController.Callback {
|
||||||
|
override fun change() {
|
||||||
|
sendAdd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
assertThat(signal).isNull()
|
||||||
|
|
||||||
|
controller.callbacks.first().change()
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun strategyIfNotAdded() {
|
||||||
|
val underTest =
|
||||||
|
object :
|
||||||
|
CallbackControllerAutoAddable<TestableController.Callback, TestableController>(
|
||||||
|
TestableController()
|
||||||
|
) {
|
||||||
|
override val description: String = ""
|
||||||
|
override val spec: TileSpec
|
||||||
|
get() = SPEC
|
||||||
|
|
||||||
|
override fun ProducerScope<AutoAddSignal>.getCallback():
|
||||||
|
TestableController.Callback {
|
||||||
|
return object : TestableController.Callback {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.IfNotAdded(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestableController : CallbackController<TestableController.Callback> {
|
||||||
|
|
||||||
|
val callbacks = mutableSetOf<Callback>()
|
||||||
|
|
||||||
|
override fun addCallback(listener: Callback) {
|
||||||
|
callbacks.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeCallback(listener: Callback) {
|
||||||
|
callbacks.remove(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
fun change() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPEC = TileSpec.create("test")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.CastTile
|
||||||
|
import com.android.systemui.statusbar.policy.CastController
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
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.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class CastAutoAddableTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var castController: CastController
|
||||||
|
@Captor private lateinit var callbackCaptor: ArgumentCaptor<CastController.Callback>
|
||||||
|
|
||||||
|
private lateinit var underTest: CastAutoAddable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
underTest = CastAutoAddable(castController)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onCastDevicesChanged_noDevices_noSignal() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(castController).addCallback(capture(callbackCaptor))
|
||||||
|
|
||||||
|
callbackCaptor.value.onCastDevicesChanged()
|
||||||
|
|
||||||
|
assertThat(signal).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onCastDevicesChanged_deviceNotConnectedOrConnecting_noSignal() = runTest {
|
||||||
|
val device =
|
||||||
|
CastController.CastDevice().apply {
|
||||||
|
state = CastController.CastDevice.STATE_DISCONNECTED
|
||||||
|
}
|
||||||
|
whenever(castController.castDevices).thenReturn(listOf(device))
|
||||||
|
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(castController).addCallback(capture(callbackCaptor))
|
||||||
|
|
||||||
|
callbackCaptor.value.onCastDevicesChanged()
|
||||||
|
|
||||||
|
assertThat(signal).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onCastDevicesChanged_someDeviceConnecting_addSignal() = runTest {
|
||||||
|
val disconnectedDevice =
|
||||||
|
CastController.CastDevice().apply {
|
||||||
|
state = CastController.CastDevice.STATE_DISCONNECTED
|
||||||
|
}
|
||||||
|
val connectingDevice =
|
||||||
|
CastController.CastDevice().apply { state = CastController.CastDevice.STATE_CONNECTING }
|
||||||
|
whenever(castController.castDevices)
|
||||||
|
.thenReturn(listOf(disconnectedDevice, connectingDevice))
|
||||||
|
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(castController).addCallback(capture(callbackCaptor))
|
||||||
|
|
||||||
|
callbackCaptor.value.onCastDevicesChanged()
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onCastDevicesChanged_someDeviceConnected_addSignal() = runTest {
|
||||||
|
val disconnectedDevice =
|
||||||
|
CastController.CastDevice().apply {
|
||||||
|
state = CastController.CastDevice.STATE_DISCONNECTED
|
||||||
|
}
|
||||||
|
val connectedDevice =
|
||||||
|
CastController.CastDevice().apply { state = CastController.CastDevice.STATE_CONNECTED }
|
||||||
|
whenever(castController.castDevices).thenReturn(listOf(disconnectedDevice, connectedDevice))
|
||||||
|
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(castController).addCallback(capture(callbackCaptor))
|
||||||
|
|
||||||
|
callbackCaptor.value.onCastDevicesChanged()
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPEC = TileSpec.create(CastTile.TILE_SPEC)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.DataSaverTile
|
||||||
|
import com.android.systemui.statusbar.policy.DataSaverController
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
|
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.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class DataSaverAutoAddableTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var dataSaverController: DataSaverController
|
||||||
|
@Captor private lateinit var callbackCaptor: ArgumentCaptor<DataSaverController.Listener>
|
||||||
|
|
||||||
|
private lateinit var underTest: DataSaverAutoAddable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
underTest = DataSaverAutoAddable(dataSaverController)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dataSaverNotEnabled_NoSignal() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(dataSaverController).addCallback(capture(callbackCaptor))
|
||||||
|
|
||||||
|
callbackCaptor.value.onDataSaverChanged(false)
|
||||||
|
|
||||||
|
assertThat(signal).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dataSaverEnabled_addSignal() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(dataSaverController).addCallback(capture(callbackCaptor))
|
||||||
|
|
||||||
|
callbackCaptor.value.onDataSaverChanged(true)
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPEC = TileSpec.create(DataSaverTile.TILE_SPEC)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
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
|
||||||
|
import com.android.systemui.qs.tiles.DeviceControlsTile
|
||||||
|
import com.android.systemui.statusbar.policy.DeviceControlsController
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
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.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class DeviceControlsAutoAddableTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var deviceControlsController: DeviceControlsController
|
||||||
|
@Captor private lateinit var callbackCaptor: ArgumentCaptor<DeviceControlsController.Callback>
|
||||||
|
|
||||||
|
private lateinit var underTest: DeviceControlsAutoAddable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
underTest = DeviceControlsAutoAddable(deviceControlsController)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun strategyAlways() {
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Always)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onControlsUpdate_position_addSignal() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
val position = 5
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(deviceControlsController).setCallback(capture(callbackCaptor))
|
||||||
|
callbackCaptor.value.onControlsUpdate(position)
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC, position))
|
||||||
|
verify(deviceControlsController).removeCallback()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onControlsUpdate_nullPosition_noAddSignal() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(deviceControlsController).setCallback(capture(callbackCaptor))
|
||||||
|
callbackCaptor.value.onControlsUpdate(null)
|
||||||
|
|
||||||
|
assertThat(signal).isNull()
|
||||||
|
verify(deviceControlsController).removeCallback()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onRemoveControlsAutoTracker_removeSignal() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(deviceControlsController).setCallback(capture(callbackCaptor))
|
||||||
|
callbackCaptor.value.removeControlsAutoTracker()
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Remove(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun flowCancelled_removeCallback() = runTest {
|
||||||
|
val job = launch { underTest.autoAddSignal(0).collect() }
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
runCurrent()
|
||||||
|
verify(deviceControlsController).removeCallback()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPEC = TileSpec.create(DeviceControlsTile.TILE_SPEC)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
|
||||||
|
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||||
|
import com.android.systemui.qs.tiles.HotspotTile
|
||||||
|
import com.android.systemui.statusbar.policy.HotspotController
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
|
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.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class HotspotAutoAddableTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var hotspotController: HotspotController
|
||||||
|
@Captor private lateinit var callbackCaptor: ArgumentCaptor<HotspotController.Callback>
|
||||||
|
|
||||||
|
private lateinit var underTest: HotspotAutoAddable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
underTest = HotspotAutoAddable(hotspotController)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun enabled_addSignal() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(hotspotController).addCallback(capture(callbackCaptor))
|
||||||
|
callbackCaptor.value.onHotspotChanged(/* enabled = */ true, /* numDevices = */ 5)
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun notEnabled_noSignal() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(hotspotController).addCallback(capture(callbackCaptor))
|
||||||
|
callbackCaptor.value.onHotspotChanged(/* enabled = */ false, /* numDevices = */ 0)
|
||||||
|
|
||||||
|
assertThat(signal).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPEC = TileSpec.create(HotspotTile.TILE_SPEC)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.hardware.display.NightDisplayListener
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
import com.android.systemui.dagger.NightDisplayListenerModule
|
||||||
|
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
|
||||||
|
import com.android.systemui.qs.tiles.NightDisplayTile
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.test.TestResult
|
||||||
|
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.Answers
|
||||||
|
import org.mockito.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.inOrder
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class NightDisplayAutoAddableTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock(answer = Answers.RETURNS_SELF)
|
||||||
|
private lateinit var nightDisplayListenerBuilder: NightDisplayListenerModule.Builder
|
||||||
|
@Mock private lateinit var nightDisplayListener: NightDisplayListener
|
||||||
|
@Captor private lateinit var callbackCaptor: ArgumentCaptor<NightDisplayListener.Callback>
|
||||||
|
|
||||||
|
private lateinit var underTest: NightDisplayAutoAddable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
whenever(nightDisplayListenerBuilder.build()).thenReturn(nightDisplayListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun disabled_strategyDisabled() =
|
||||||
|
testWithFeatureAvailability(enabled = false) {
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Disabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun enabled_strategyIfNotAdded() = testWithFeatureAvailability {
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.IfNotAdded(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun listenerCreatedForCorrectUser() = testWithFeatureAvailability {
|
||||||
|
val user = 42
|
||||||
|
backgroundScope.launch { underTest.autoAddSignal(user).collect() }
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
val inOrder = inOrder(nightDisplayListenerBuilder)
|
||||||
|
inOrder.verify(nightDisplayListenerBuilder).setUser(user)
|
||||||
|
inOrder.verify(nightDisplayListenerBuilder).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onCancelFlow_removeCallback() = testWithFeatureAvailability {
|
||||||
|
val job = launch { underTest.autoAddSignal(0).collect() }
|
||||||
|
runCurrent()
|
||||||
|
job.cancel()
|
||||||
|
runCurrent()
|
||||||
|
verify(nightDisplayListener).setCallback(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onActivatedTrue_addSignal() = testWithFeatureAvailability {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(nightDisplayListener).setCallback(capture(callbackCaptor))
|
||||||
|
callbackCaptor.value.onActivated(true)
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testWithFeatureAvailability(
|
||||||
|
enabled: Boolean = true,
|
||||||
|
body: suspend TestScope.() -> TestResult
|
||||||
|
) = runTest {
|
||||||
|
context.orCreateTestableResources.addOverride(
|
||||||
|
com.android.internal.R.bool.config_nightDisplayAvailable,
|
||||||
|
enabled
|
||||||
|
)
|
||||||
|
underTest = NightDisplayAutoAddable(nightDisplayListenerBuilder, context)
|
||||||
|
body()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPEC = TileSpec.create(NightDisplayTile.TILE_SPEC)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
import com.android.systemui.qs.ReduceBrightColorsController
|
||||||
|
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
|
||||||
|
import com.android.systemui.qs.tiles.ReduceBrightColorsTile
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestResult
|
||||||
|
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.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class ReduceBrightColorsAutoAddableTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var reduceBrightColorsController: ReduceBrightColorsController
|
||||||
|
@Captor
|
||||||
|
private lateinit var reduceBrightColorsListenerCaptor:
|
||||||
|
ArgumentCaptor<ReduceBrightColorsController.Listener>
|
||||||
|
|
||||||
|
private lateinit var underTest: ReduceBrightColorsAutoAddable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun notAvailable_strategyDisabled() =
|
||||||
|
testWithFeatureAvailability(available = false) {
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Disabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun available_strategyIfNotAdded() =
|
||||||
|
testWithFeatureAvailability(available = true) {
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.IfNotAdded(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun activated_addSignal() = testWithFeatureAvailability {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(reduceBrightColorsController).addCallback(capture(reduceBrightColorsListenerCaptor))
|
||||||
|
|
||||||
|
reduceBrightColorsListenerCaptor.value.onActivated(true)
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun notActivated_noSignal() = testWithFeatureAvailability {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(reduceBrightColorsController).addCallback(capture(reduceBrightColorsListenerCaptor))
|
||||||
|
|
||||||
|
reduceBrightColorsListenerCaptor.value.onActivated(false)
|
||||||
|
|
||||||
|
assertThat(signal).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testWithFeatureAvailability(
|
||||||
|
available: Boolean = true,
|
||||||
|
body: suspend TestScope.() -> TestResult
|
||||||
|
) = runTest {
|
||||||
|
underTest = ReduceBrightColorsAutoAddable(reduceBrightColorsController, available)
|
||||||
|
body()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPEC = TileSpec.create(ReduceBrightColorsTile.TILE_SPEC)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.content.ComponentName
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
import com.android.systemui.coroutines.collectValues
|
||||||
|
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
|
||||||
|
import com.android.systemui.statusbar.policy.SafetyController
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
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.ArgumentCaptor
|
||||||
|
import org.mockito.Captor
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class SafetyCenterAutoAddableTest : SysuiTestCase() {
|
||||||
|
private val testDispatcher = StandardTestDispatcher()
|
||||||
|
private val testScope = TestScope(testDispatcher)
|
||||||
|
|
||||||
|
@Mock private lateinit var safetyController: SafetyController
|
||||||
|
@Mock private lateinit var packageManager: PackageManager
|
||||||
|
@Captor
|
||||||
|
private lateinit var safetyControllerListenerCaptor: ArgumentCaptor<SafetyController.Listener>
|
||||||
|
|
||||||
|
private lateinit var underTest: SafetyCenterAutoAddable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
context.ensureTestableResources()
|
||||||
|
|
||||||
|
// Set these by default, will also test special cases
|
||||||
|
context.orCreateTestableResources.addOverride(
|
||||||
|
R.string.safety_quick_settings_tile_class,
|
||||||
|
SAFETY_TILE_CLASS_NAME
|
||||||
|
)
|
||||||
|
whenever(packageManager.permissionControllerPackageName)
|
||||||
|
.thenReturn(PERMISSION_CONTROLLER_PACKAGE_NAME)
|
||||||
|
|
||||||
|
underTest =
|
||||||
|
SafetyCenterAutoAddable(
|
||||||
|
safetyController,
|
||||||
|
packageManager,
|
||||||
|
context.resources,
|
||||||
|
testDispatcher,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun strategyAlwaysTrack() =
|
||||||
|
testScope.runTest {
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Always)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun tileAlwaysAdded() =
|
||||||
|
testScope.runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun safetyCenterDisabled_removeSignal() =
|
||||||
|
testScope.runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(safetyController).addCallback(capture(safetyControllerListenerCaptor))
|
||||||
|
safetyControllerListenerCaptor.value.onSafetyCenterEnableChanged(false)
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Remove(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun safetyCenterEnabled_newAddSignal() =
|
||||||
|
testScope.runTest {
|
||||||
|
val signals by collectValues(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(safetyController).addCallback(capture(safetyControllerListenerCaptor))
|
||||||
|
safetyControllerListenerCaptor.value.onSafetyCenterEnableChanged(true)
|
||||||
|
|
||||||
|
assertThat(signals.size).isEqualTo(2)
|
||||||
|
assertThat(signals.last()).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun flowCancelled_removeListener() =
|
||||||
|
testScope.runTest {
|
||||||
|
val job = launch { underTest.autoAddSignal(0).collect() }
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(safetyController).addCallback(capture(safetyControllerListenerCaptor))
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
runCurrent()
|
||||||
|
verify(safetyController).removeCallback(safetyControllerListenerCaptor.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun emptyClassName_noSignals() =
|
||||||
|
testScope.runTest {
|
||||||
|
context.orCreateTestableResources.addOverride(
|
||||||
|
R.string.safety_quick_settings_tile_class,
|
||||||
|
""
|
||||||
|
)
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
runCurrent()
|
||||||
|
|
||||||
|
verify(safetyController, never()).addCallback(any())
|
||||||
|
|
||||||
|
assertThat(signal).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SAFETY_TILE_CLASS_NAME = "cls"
|
||||||
|
private const val PERMISSION_CONTROLLER_PACKAGE_NAME = "pkg"
|
||||||
|
private val SPEC =
|
||||||
|
TileSpec.create(
|
||||||
|
ComponentName(PERMISSION_CONTROLLER_PACKAGE_NAME, SAFETY_TILE_CLASS_NAME)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
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
|
||||||
|
import com.android.systemui.qs.tiles.QuickAccessWalletTile
|
||||||
|
import com.android.systemui.statusbar.policy.WalletController
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class WalletAutoAddableTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
@Mock private lateinit var walletController: WalletController
|
||||||
|
|
||||||
|
private lateinit var underTest: WalletAutoAddable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
underTest = WalletAutoAddable(walletController)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun strategyIfNotAdded() {
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.IfNotAdded(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun walletPositionNull_noSignal() = runTest {
|
||||||
|
whenever(walletController.getWalletPosition()).thenReturn(null)
|
||||||
|
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
|
||||||
|
assertThat(signal).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun walletPositionNumber_addedInThatPosition() = runTest {
|
||||||
|
val position = 4
|
||||||
|
whenever(walletController.getWalletPosition()).thenReturn(4)
|
||||||
|
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC, position))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPEC = TileSpec.create(QuickAccessWalletTile.TILE_SPEC)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.autoaddable
|
||||||
|
|
||||||
|
import android.content.pm.UserInfo
|
||||||
|
import android.content.pm.UserInfo.FLAG_FULL
|
||||||
|
import android.content.pm.UserInfo.FLAG_MANAGED_PROFILE
|
||||||
|
import android.content.pm.UserInfo.FLAG_PRIMARY
|
||||||
|
import android.content.pm.UserInfo.FLAG_PROFILE
|
||||||
|
import android.testing.AndroidTestingRunner
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.coroutines.collectLastValue
|
||||||
|
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
|
||||||
|
import com.android.systemui.qs.tiles.WorkModeTile
|
||||||
|
import com.android.systemui.settings.FakeUserTracker
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
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(AndroidTestingRunner::class)
|
||||||
|
class WorkTileAutoAddableTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private lateinit var userTracker: FakeUserTracker
|
||||||
|
|
||||||
|
private lateinit var underTest: WorkTileAutoAddable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
userTracker =
|
||||||
|
FakeUserTracker(
|
||||||
|
_userId = USER_INFO_0.id,
|
||||||
|
_userInfo = USER_INFO_0,
|
||||||
|
_userProfiles = listOf(USER_INFO_0)
|
||||||
|
)
|
||||||
|
|
||||||
|
underTest = WorkTileAutoAddable(userTracker)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun changeInProfiles_hasManagedProfile_sendsAddSignal() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
|
||||||
|
userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun changeInProfiles_noManagedProfile_sendsRemoveSignal() = runTest {
|
||||||
|
userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
|
||||||
|
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
|
||||||
|
userTracker.set(listOf(USER_INFO_0), selectedUserIndex = 0)
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Remove(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun startingWithManagedProfile_sendsAddSignal() = runTest {
|
||||||
|
userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
|
||||||
|
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
|
||||||
|
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun userChangeToUserWithProfile_noSignalForOriginalUser() = runTest {
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
|
||||||
|
userTracker.set(listOf(USER_INFO_1, USER_INFO_WORK), selectedUserIndex = 0)
|
||||||
|
|
||||||
|
assertThat(signal).isNotEqualTo(AutoAddSignal.Add(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun userChangeToUserWithoutProfile_noSignalForOriginalUser() = runTest {
|
||||||
|
userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
|
||||||
|
val signal by collectLastValue(underTest.autoAddSignal(0))
|
||||||
|
|
||||||
|
userTracker.set(listOf(USER_INFO_1), selectedUserIndex = 0)
|
||||||
|
|
||||||
|
assertThat(signal).isNotEqualTo(AutoAddSignal.Remove(SPEC))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun strategyAlways() {
|
||||||
|
assertThat(underTest.autoAddTracking).isEqualTo(AutoAddTracking.Always)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<AutoAddable>): AutoAddInteractor {
|
||||||
|
return AutoAddInteractor(
|
||||||
|
autoAddables,
|
||||||
|
autoAddRepository,
|
||||||
|
dumpManager,
|
||||||
|
logger,
|
||||||
|
testScope.backgroundScope
|
||||||
|
)
|
||||||
|
.apply { init(currentTilesInteractor) }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SPEC = TileSpec.create("spec")
|
||||||
|
private val USER = 10
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -100,6 +100,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
|
|||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
featureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true)
|
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))
|
userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1))
|
||||||
|
|
||||||
|
|||||||
@@ -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<Int, MutableStateFlow<Set<TileSpec>>>()
|
||||||
|
|
||||||
|
override fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
|
||||||
|
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<Set<TileSpec>> =
|
||||||
|
autoAddedTilesPerUser.getOrPut(userId) { MutableStateFlow(emptySet()) }
|
||||||
|
}
|
||||||
@@ -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.autoaddable
|
||||||
|
|
||||||
|
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
|
||||||
|
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.TileSpec
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
|
|
||||||
|
class FakeAutoAddable(
|
||||||
|
private val spec: TileSpec,
|
||||||
|
override val autoAddTracking: AutoAddTracking,
|
||||||
|
) : AutoAddable {
|
||||||
|
|
||||||
|
private val signalsPerUser = mutableMapOf<Int, MutableStateFlow<AutoAddSignal?>>()
|
||||||
|
private fun getFlow(userId: Int): MutableStateFlow<AutoAddSignal?> =
|
||||||
|
signalsPerUser.getOrPut(userId) { MutableStateFlow(null) }
|
||||||
|
|
||||||
|
override fun autoAddSignal(userId: Int): Flow<AutoAddSignal> {
|
||||||
|
return getFlow(userId).asStateFlow().filterNotNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun sendRemoveSignal(userId: Int) {
|
||||||
|
getFlow(userId).value = AutoAddSignal.Remove(spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun sendAddSignal(userId: Int, position: Int = POSITION_AT_END) {
|
||||||
|
getFlow(userId).value = AutoAddSignal.Add(spec, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val description: String
|
||||||
|
get() = "FakeAutoAddable($spec)"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user