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.
|
||||
tunerService.addTunable(this, TILES_SETTING);
|
||||
// AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
|
||||
mAutoTiles = autoTiles.get();
|
||||
if (!mFeatureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)) {
|
||||
mAutoTiles = autoTiles.get();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.log.LogBuffer
|
||||
import com.android.systemui.log.LogBufferFactory
|
||||
import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository
|
||||
import com.android.systemui.qs.pipeline.data.repository.AutoAddSettingRepository
|
||||
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
|
||||
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.Multibinds
|
||||
|
||||
@Module
|
||||
@Module(
|
||||
includes =
|
||||
[
|
||||
BaseAutoAddableModule::class,
|
||||
]
|
||||
)
|
||||
abstract class QSAutoAddModule {
|
||||
|
||||
@Binds abstract fun bindAutoAddRepository(impl: AutoAddSettingRepository): AutoAddRepository
|
||||
|
||||
@Multibinds abstract fun providesAutoAddableSet(): Set<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.domain.interactor.CurrentTilesInteractor
|
||||
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 dagger.Binds
|
||||
import dagger.Module
|
||||
@@ -53,8 +53,8 @@ abstract class QSPipelineModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(PrototypeCoreStartable::class)
|
||||
abstract fun providePrototypeCoreStartable(startable: PrototypeCoreStartable): CoreStartable
|
||||
@ClassKey(QSPipelineCoreStartable::class)
|
||||
abstract fun provideCoreStartable(startable: QSPipelineCoreStartable): CoreStartable
|
||||
|
||||
companion object {
|
||||
/**
|
||||
|
||||
@@ -19,5 +19,5 @@ import java.lang.annotation.Retention
|
||||
import java.lang.annotation.RetentionPolicy
|
||||
import javax.inject.Qualifier
|
||||
|
||||
/** A {@link LogBuffer} for the new QS Pipeline for logging changes to the set of current tiles. */
|
||||
/** A [LogBuffer] for the new QS Pipeline for logging changes to the set of current tiles. */
|
||||
@Qualifier @MustBeDocumented @Retention(RetentionPolicy.RUNTIME) annotation class QSTileListLog
|
||||
|
||||
@@ -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(
|
||||
override val spec: String,
|
||||
val componentName: ComponentName,
|
||||
) : TileSpec(spec)
|
||||
) : TileSpec(spec) {
|
||||
override fun toString(): String {
|
||||
return "CustomTileSpec(${componentName.toShortString()})"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/** Create a [TileSpec] from the string [spec]. */
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.qs.pipeline.shared.logging
|
||||
import android.annotation.UserIdInt
|
||||
import com.android.systemui.log.LogBuffer
|
||||
import com.android.systemui.log.LogLevel
|
||||
import com.android.systemui.qs.pipeline.dagger.QSAutoAddLog
|
||||
import com.android.systemui.qs.pipeline.dagger.QSTileListLog
|
||||
import com.android.systemui.qs.pipeline.shared.TileSpec
|
||||
import javax.inject.Inject
|
||||
@@ -32,10 +33,12 @@ class QSPipelineLogger
|
||||
@Inject
|
||||
constructor(
|
||||
@QSTileListLog private val tileListLogBuffer: LogBuffer,
|
||||
@QSAutoAddLog private val tileAutoAddLogBuffer: LogBuffer,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val TILE_LIST_TAG = "QSTileListLog"
|
||||
const val AUTO_ADD_TAG = "QSAutoAddableLog"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,6 +139,31 @@ constructor(
|
||||
)
|
||||
}
|
||||
|
||||
fun logTileAutoAdded(userId: Int, spec: TileSpec, position: Int) {
|
||||
tileAutoAddLogBuffer.log(
|
||||
AUTO_ADD_TAG,
|
||||
LogLevel.DEBUG,
|
||||
{
|
||||
int1 = userId
|
||||
int2 = position
|
||||
str1 = spec.toString()
|
||||
},
|
||||
{ "Tile $str1 auto added for user $int1 at position $int2" }
|
||||
)
|
||||
}
|
||||
|
||||
fun logTileAutoRemoved(userId: Int, spec: TileSpec) {
|
||||
tileAutoAddLogBuffer.log(
|
||||
AUTO_ADD_TAG,
|
||||
LogLevel.DEBUG,
|
||||
{
|
||||
int1 = userId
|
||||
str1 = spec.toString()
|
||||
},
|
||||
{ "Tile $str1 auto removed for user $int1" }
|
||||
)
|
||||
}
|
||||
|
||||
/** Reasons for destroying an existing tile. */
|
||||
enum class TileDestroyedReason(val readable: String) {
|
||||
TILE_REMOVED("Tile removed from current set"),
|
||||
|
||||
@@ -140,6 +140,7 @@ public class QSTileHostTest extends SysuiTestCase {
|
||||
mFeatureFlags = new FakeFeatureFlags();
|
||||
|
||||
mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false);
|
||||
mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false);
|
||||
|
||||
mMainExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
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))
|
||||
|
||||
|
||||
@@ -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