Add AutoAddables
AutoAddable encapsulates a particular tile that can be auto-added to Quick Settings and the triggers needed for it. Test: atest com.android.systemui.qs.pipeline.interactor.autoaddable Bug: 278577795 Change-Id: I99d3856c2b71a8604c586e8b6df2306e726168e9
This commit is contained in:
@@ -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,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,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
|
||||
}
|
||||
@@ -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]. */
|
||||
|
||||
@@ -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,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