[Sb refactor] Add a demo command flow method
This CL mainly just adds a single method to DemoModeController, `demoFlowForCommand(String)`. The rest of the diff is from a combination of ktlint formatting and writing a test for the flow, which required some restructuring of the class. Test: DemoModeControllerTest Bug: 238425913 Change-Id: I40dd0b42a881d0776e9b19b282496e850a67a981
This commit is contained in:
@@ -20,7 +20,7 @@ import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
|
||||
/**
|
||||
* Class to track the availability of [DemoMode]. Use this class to track the availability and
|
||||
@@ -29,7 +29,10 @@ import android.provider.Settings
|
||||
* This class works by wrapping a content observer for the relevant keys related to DemoMode
|
||||
* availability and current on/off state, and triggering callbacks.
|
||||
*/
|
||||
abstract class DemoModeAvailabilityTracker(val context: Context) {
|
||||
abstract class DemoModeAvailabilityTracker(
|
||||
val context: Context,
|
||||
val globalSettings: GlobalSettings,
|
||||
) {
|
||||
var isInDemoMode = false
|
||||
var isDemoModeAvailable = false
|
||||
|
||||
@@ -41,9 +44,9 @@ abstract class DemoModeAvailabilityTracker(val context: Context) {
|
||||
fun startTracking() {
|
||||
val resolver = context.contentResolver
|
||||
resolver.registerContentObserver(
|
||||
Settings.Global.getUriFor(DEMO_MODE_ALLOWED), false, allowedObserver)
|
||||
globalSettings.getUriFor(DEMO_MODE_ALLOWED), false, allowedObserver)
|
||||
resolver.registerContentObserver(
|
||||
Settings.Global.getUriFor(DEMO_MODE_ON), false, onObserver)
|
||||
globalSettings.getUriFor(DEMO_MODE_ON), false, onObserver)
|
||||
}
|
||||
|
||||
fun stopTracking() {
|
||||
@@ -57,12 +60,11 @@ abstract class DemoModeAvailabilityTracker(val context: Context) {
|
||||
abstract fun onDemoModeFinished()
|
||||
|
||||
private fun checkIsDemoModeAllowed(): Boolean {
|
||||
return Settings.Global
|
||||
.getInt(context.contentResolver, DEMO_MODE_ALLOWED, 0) != 0
|
||||
return globalSettings.getInt(DEMO_MODE_ALLOWED, 0) != 0
|
||||
}
|
||||
|
||||
private fun checkIsDemoModeOn(): Boolean {
|
||||
return Settings.Global.getInt(context.contentResolver, DEMO_MODE_ON, 0) != 0
|
||||
return globalSettings.getInt(DEMO_MODE_ON, 0) != 0
|
||||
}
|
||||
|
||||
private val allowedObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
|
||||
|
||||
@@ -24,22 +24,28 @@ import android.os.Bundle
|
||||
import android.os.UserHandle
|
||||
import android.util.Log
|
||||
import com.android.systemui.Dumpable
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import com.android.systemui.demomode.DemoMode.ACTION_DEMO
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.statusbar.policy.CallbackController
|
||||
import com.android.systemui.util.Assert
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import java.io.PrintWriter
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Handles system broadcasts for [DemoMode]
|
||||
*
|
||||
* Injected via [DemoModeModule]
|
||||
*/
|
||||
class DemoModeController constructor(
|
||||
class DemoModeController
|
||||
constructor(
|
||||
private val context: Context,
|
||||
private val dumpManager: DumpManager,
|
||||
private val globalSettings: GlobalSettings
|
||||
private val globalSettings: GlobalSettings,
|
||||
private val broadcastDispatcher: BroadcastDispatcher,
|
||||
) : CallbackController<DemoMode>, Dumpable {
|
||||
|
||||
// Var updated when the availability tracker changes, or when we enter/exit demo mode in-process
|
||||
@@ -58,9 +64,7 @@ class DemoModeController constructor(
|
||||
requestFinishDemoMode()
|
||||
|
||||
val m = mutableMapOf<String, MutableList<DemoMode>>()
|
||||
DemoMode.COMMANDS.map { command ->
|
||||
m.put(command, mutableListOf())
|
||||
}
|
||||
DemoMode.COMMANDS.map { command -> m.put(command, mutableListOf()) }
|
||||
receiverMap = m
|
||||
}
|
||||
|
||||
@@ -71,7 +75,7 @@ class DemoModeController constructor(
|
||||
|
||||
initialized = true
|
||||
|
||||
dumpManager.registerDumpable(TAG, this)
|
||||
dumpManager.registerNormalDumpable(TAG, this)
|
||||
|
||||
// Due to DemoModeFragment running in systemui:tuner process, we have to observe for
|
||||
// content changes to know if the setting turned on or off
|
||||
@@ -81,8 +85,13 @@ class DemoModeController constructor(
|
||||
|
||||
val demoFilter = IntentFilter()
|
||||
demoFilter.addAction(ACTION_DEMO)
|
||||
context.registerReceiverAsUser(broadcastReceiver, UserHandle.ALL, demoFilter,
|
||||
android.Manifest.permission.DUMP, null, Context.RECEIVER_EXPORTED)
|
||||
|
||||
broadcastDispatcher.registerReceiver(
|
||||
receiver = broadcastReceiver,
|
||||
filter = demoFilter,
|
||||
user = UserHandle.ALL,
|
||||
permission = android.Manifest.permission.DUMP,
|
||||
)
|
||||
}
|
||||
|
||||
override fun addCallback(listener: DemoMode) {
|
||||
@@ -91,16 +100,15 @@ class DemoModeController constructor(
|
||||
|
||||
commands.forEach { command ->
|
||||
if (!receiverMap.containsKey(command)) {
|
||||
throw IllegalStateException("Command ($command) not recognized. " +
|
||||
"See DemoMode.java for valid commands")
|
||||
throw IllegalStateException(
|
||||
"Command ($command) not recognized. " + "See DemoMode.java for valid commands"
|
||||
)
|
||||
}
|
||||
|
||||
receiverMap[command]!!.add(listener)
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
receivers.add(listener)
|
||||
}
|
||||
synchronized(this) { receivers.add(listener) }
|
||||
|
||||
if (isInDemoMode) {
|
||||
listener.onDemoModeStarted()
|
||||
@@ -109,14 +117,46 @@ class DemoModeController constructor(
|
||||
|
||||
override fun removeCallback(listener: DemoMode) {
|
||||
synchronized(this) {
|
||||
listener.demoCommands().forEach { command ->
|
||||
receiverMap[command]!!.remove(listener)
|
||||
}
|
||||
listener.demoCommands().forEach { command -> receiverMap[command]!!.remove(listener) }
|
||||
|
||||
receivers.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a [Flow] for the stream of demo mode arguments that come in for the given [command]
|
||||
*
|
||||
* This is equivalent of creating a listener manually and adding an event handler for the given
|
||||
* command, like so:
|
||||
*
|
||||
* ```
|
||||
* class Demoable {
|
||||
* private val demoHandler = object : DemoMode {
|
||||
* override fun demoCommands() = listOf(<command>)
|
||||
*
|
||||
* override fun dispatchDemoCommand(command: String, args: Bundle) {
|
||||
* handleDemoCommand(args)
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param command The top-level demo mode command you want a stream for
|
||||
*/
|
||||
fun demoFlowForCommand(command: String): Flow<Bundle> = conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : DemoMode {
|
||||
override fun demoCommands(): List<String> = listOf(command)
|
||||
|
||||
override fun dispatchDemoCommand(command: String, args: Bundle) {
|
||||
trySend(args)
|
||||
}
|
||||
}
|
||||
|
||||
addCallback(callback)
|
||||
awaitClose { removeCallback(callback) }
|
||||
}
|
||||
|
||||
private fun setIsDemoModeAllowed(enabled: Boolean) {
|
||||
// Turn off demo mode if it was on
|
||||
if (isInDemoMode && !enabled) {
|
||||
@@ -129,13 +169,9 @@ class DemoModeController constructor(
|
||||
Assert.isMainThread()
|
||||
|
||||
val copy: List<DemoModeCommandReceiver>
|
||||
synchronized(this) {
|
||||
copy = receivers.toList()
|
||||
}
|
||||
synchronized(this) { copy = receivers.toList() }
|
||||
|
||||
copy.forEach { r ->
|
||||
r.onDemoModeStarted()
|
||||
}
|
||||
copy.forEach { r -> r.onDemoModeStarted() }
|
||||
}
|
||||
|
||||
private fun exitDemoMode() {
|
||||
@@ -143,18 +179,13 @@ class DemoModeController constructor(
|
||||
Assert.isMainThread()
|
||||
|
||||
val copy: List<DemoModeCommandReceiver>
|
||||
synchronized(this) {
|
||||
copy = receivers.toList()
|
||||
}
|
||||
synchronized(this) { copy = receivers.toList() }
|
||||
|
||||
copy.forEach { r ->
|
||||
r.onDemoModeFinished()
|
||||
}
|
||||
copy.forEach { r -> r.onDemoModeFinished() }
|
||||
}
|
||||
|
||||
fun dispatchDemoCommand(command: String, args: Bundle) {
|
||||
Assert.isMainThread()
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "dispatchDemoCommand: $command, args=$args")
|
||||
}
|
||||
@@ -172,9 +203,7 @@ class DemoModeController constructor(
|
||||
}
|
||||
|
||||
// See? demo mode is easy now, you just notify the listeners when their command is called
|
||||
receiverMap[command]!!.forEach { receiver ->
|
||||
receiver.dispatchDemoCommand(command, args)
|
||||
}
|
||||
receiverMap[command]!!.forEach { receiver -> receiver.dispatchDemoCommand(command, args) }
|
||||
}
|
||||
|
||||
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
||||
@@ -183,65 +212,64 @@ class DemoModeController constructor(
|
||||
pw.println(" isDemoModeAllowed=$isAvailable")
|
||||
pw.print(" receivers=[")
|
||||
val copy: List<DemoModeCommandReceiver>
|
||||
synchronized(this) {
|
||||
copy = receivers.toList()
|
||||
}
|
||||
copy.forEach { recv ->
|
||||
pw.print(" ${recv.javaClass.simpleName}")
|
||||
}
|
||||
synchronized(this) { copy = receivers.toList() }
|
||||
copy.forEach { recv -> pw.print(" ${recv.javaClass.simpleName}") }
|
||||
pw.println(" ]")
|
||||
pw.println(" receiverMap= [")
|
||||
receiverMap.keys.forEach { command ->
|
||||
pw.print(" $command : [")
|
||||
val recvs = receiverMap[command]!!.map { receiver ->
|
||||
receiver.javaClass.simpleName
|
||||
}.joinToString(",")
|
||||
val recvs =
|
||||
receiverMap[command]!!
|
||||
.map { receiver -> receiver.javaClass.simpleName }
|
||||
.joinToString(",")
|
||||
pw.println("$recvs ]")
|
||||
}
|
||||
}
|
||||
|
||||
private val tracker = object : DemoModeAvailabilityTracker(context) {
|
||||
override fun onDemoModeAvailabilityChanged() {
|
||||
setIsDemoModeAllowed(isDemoModeAvailable)
|
||||
}
|
||||
private val tracker =
|
||||
object : DemoModeAvailabilityTracker(context, globalSettings) {
|
||||
override fun onDemoModeAvailabilityChanged() {
|
||||
setIsDemoModeAllowed(isDemoModeAvailable)
|
||||
}
|
||||
|
||||
override fun onDemoModeStarted() {
|
||||
if (this@DemoModeController.isInDemoMode != isInDemoMode) {
|
||||
enterDemoMode()
|
||||
override fun onDemoModeStarted() {
|
||||
if (this@DemoModeController.isInDemoMode != isInDemoMode) {
|
||||
enterDemoMode()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDemoModeFinished() {
|
||||
if (this@DemoModeController.isInDemoMode != isInDemoMode) {
|
||||
exitDemoMode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDemoModeFinished() {
|
||||
if (this@DemoModeController.isInDemoMode != isInDemoMode) {
|
||||
exitDemoMode()
|
||||
private val broadcastReceiver =
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "onReceive: $intent")
|
||||
}
|
||||
|
||||
val action = intent.action
|
||||
if (!ACTION_DEMO.equals(action)) {
|
||||
return
|
||||
}
|
||||
|
||||
val bundle = intent.extras ?: return
|
||||
val command = bundle.getString("command", "").trim().lowercase()
|
||||
if (command.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
dispatchDemoCommand(command, bundle)
|
||||
} catch (t: Throwable) {
|
||||
Log.w(TAG, "Error running demo command, intent=$intent $t")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val broadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "onReceive: $intent")
|
||||
}
|
||||
|
||||
val action = intent.action
|
||||
if (!ACTION_DEMO.equals(action)) {
|
||||
return
|
||||
}
|
||||
|
||||
val bundle = intent.extras ?: return
|
||||
val command = bundle.getString("command", "").trim().toLowerCase()
|
||||
if (command.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
dispatchDemoCommand(command, bundle)
|
||||
} catch (t: Throwable) {
|
||||
Log.w(TAG, "Error running demo command, intent=$intent $t")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun requestSetDemoModeAllowed(allowed: Boolean) {
|
||||
setGlobal(DEMO_MODE_ALLOWED, if (allowed) 1 else 0)
|
||||
@@ -258,10 +286,12 @@ class DemoModeController constructor(
|
||||
private fun setGlobal(key: String, value: Int) {
|
||||
globalSettings.putInt(key, value)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DEMO_MODE_ALLOWED = "sysui_demo_allowed"
|
||||
const val DEMO_MODE_ON = "sysui_tuner_demo_on"
|
||||
}
|
||||
}
|
||||
|
||||
private const val TAG = "DemoModeController"
|
||||
private const val DEMO_MODE_ALLOWED = "sysui_demo_allowed"
|
||||
private const val DEMO_MODE_ON = "sysui_tuner_demo_on"
|
||||
|
||||
private const val DEBUG = false
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.demomode.dagger;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.demomode.DemoModeController;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
@@ -37,8 +38,14 @@ public abstract class DemoModeModule {
|
||||
static DemoModeController provideDemoModeController(
|
||||
Context context,
|
||||
DumpManager dumpManager,
|
||||
GlobalSettings globalSettings) {
|
||||
DemoModeController dmc = new DemoModeController(context, dumpManager, globalSettings);
|
||||
GlobalSettings globalSettings,
|
||||
BroadcastDispatcher broadcastDispatcher
|
||||
) {
|
||||
DemoModeController dmc = new DemoModeController(
|
||||
context,
|
||||
dumpManager,
|
||||
globalSettings,
|
||||
broadcastDispatcher);
|
||||
dmc.initialize();
|
||||
return /*run*/dmc;
|
||||
}
|
||||
|
||||
@@ -24,10 +24,8 @@ import android.telephony.TelephonyManager.DATA_ACTIVITY_NONE
|
||||
import android.telephony.TelephonyManager.DATA_ACTIVITY_OUT
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
import com.android.settingslib.mobile.TelephonyIcons
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.demomode.DemoMode
|
||||
import com.android.systemui.demomode.DemoMode.COMMAND_NETWORK
|
||||
import com.android.systemui.demomode.DemoModeController
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
|
||||
@@ -35,8 +33,6 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.MobileDisabled
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
@@ -52,27 +48,7 @@ constructor(
|
||||
demoModeController: DemoModeController,
|
||||
@Application scope: CoroutineScope,
|
||||
) {
|
||||
private val demoCommandStream: Flow<Bundle> = conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : DemoMode {
|
||||
override fun demoCommands(): List<String> = listOf(COMMAND_NETWORK)
|
||||
|
||||
override fun dispatchDemoCommand(command: String, args: Bundle) {
|
||||
trySend(args)
|
||||
}
|
||||
|
||||
override fun onDemoModeFinished() {
|
||||
// Handled elsewhere
|
||||
}
|
||||
|
||||
override fun onDemoModeStarted() {
|
||||
// Handled elsewhere
|
||||
}
|
||||
}
|
||||
|
||||
demoModeController.addCallback(callback)
|
||||
awaitClose { demoModeController.removeCallback(callback) }
|
||||
}
|
||||
private val demoCommandStream = demoModeController.demoFlowForCommand(COMMAND_NETWORK)
|
||||
|
||||
// If the args contains "mobile", then all of the args are relevant. It's just the way demo mode
|
||||
// commands work and it's a little silly
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.demomode.DemoMode;
|
||||
import com.android.systemui.demomode.DemoModeAvailabilityTracker;
|
||||
import com.android.systemui.demomode.DemoModeController;
|
||||
import com.android.systemui.util.settings.GlobalSettings;
|
||||
|
||||
public class DemoModeFragment extends PreferenceFragment implements OnPreferenceChangeListener {
|
||||
|
||||
@@ -54,13 +55,15 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference
|
||||
private SwitchPreference mOnSwitch;
|
||||
|
||||
private DemoModeController mDemoModeController;
|
||||
private GlobalSettings mGlobalSettings;
|
||||
private Tracker mDemoModeTracker;
|
||||
|
||||
// We are the only ones who ever call this constructor, so don't worry about the warning
|
||||
@SuppressLint("ValidFragment")
|
||||
public DemoModeFragment(DemoModeController demoModeController) {
|
||||
public DemoModeFragment(DemoModeController demoModeController, GlobalSettings globalSettings) {
|
||||
super();
|
||||
mDemoModeController = demoModeController;
|
||||
mGlobalSettings = globalSettings;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +83,7 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference
|
||||
screen.addPreference(mOnSwitch);
|
||||
setPreferenceScreen(screen);
|
||||
|
||||
mDemoModeTracker = new Tracker(context);
|
||||
mDemoModeTracker = new Tracker(context, mGlobalSettings);
|
||||
mDemoModeTracker.startTracking();
|
||||
updateDemoModeEnabled();
|
||||
updateDemoModeOn();
|
||||
@@ -202,8 +205,8 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference
|
||||
}
|
||||
|
||||
private class Tracker extends DemoModeAvailabilityTracker {
|
||||
Tracker(Context context) {
|
||||
super(context);
|
||||
Tracker(Context context, GlobalSettings globalSettings) {
|
||||
super(context, globalSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.demomode.DemoModeController;
|
||||
import com.android.systemui.fragments.FragmentService;
|
||||
import com.android.systemui.util.settings.GlobalSettings;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -44,12 +45,18 @@ public class TunerActivity extends Activity implements
|
||||
|
||||
private final DemoModeController mDemoModeController;
|
||||
private final TunerService mTunerService;
|
||||
private final GlobalSettings mGlobalSettings;
|
||||
|
||||
@Inject
|
||||
TunerActivity(DemoModeController demoModeController, TunerService tunerService) {
|
||||
TunerActivity(
|
||||
DemoModeController demoModeController,
|
||||
TunerService tunerService,
|
||||
GlobalSettings globalSettings
|
||||
) {
|
||||
super();
|
||||
mDemoModeController = demoModeController;
|
||||
mTunerService = tunerService;
|
||||
mGlobalSettings = globalSettings;
|
||||
}
|
||||
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -69,7 +76,7 @@ public class TunerActivity extends Activity implements
|
||||
boolean showDemoMode = action != null && action.equals(
|
||||
"com.android.settings.action.DEMO_MODE");
|
||||
final PreferenceFragment fragment = showDemoMode
|
||||
? new DemoModeFragment(mDemoModeController)
|
||||
? new DemoModeFragment(mDemoModeController, mGlobalSettings)
|
||||
: new TunerFragment(mTunerService);
|
||||
getFragmentManager().beginTransaction().replace(R.id.content_frame,
|
||||
fragment, TAG_TUNER).commit();
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.demomode
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.demomode.DemoMode.ACTION_DEMO
|
||||
import com.android.systemui.demomode.DemoMode.COMMAND_STATUS
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.util.settings.FakeSettings
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
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
|
||||
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@TestableLooper.RunWithLooper
|
||||
@SmallTest
|
||||
class DemoModeControllerTest : SysuiTestCase() {
|
||||
private lateinit var underTest: DemoModeController
|
||||
|
||||
@Mock private lateinit var dumpManager: DumpManager
|
||||
|
||||
private val globalSettings = FakeSettings()
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
allowTestableLooperAsMainThread()
|
||||
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
globalSettings.putInt(DemoModeController.DEMO_MODE_ALLOWED, 1)
|
||||
globalSettings.putInt(DemoModeController.DEMO_MODE_ON, 1)
|
||||
|
||||
underTest =
|
||||
DemoModeController(
|
||||
context = context,
|
||||
dumpManager = dumpManager,
|
||||
globalSettings = globalSettings,
|
||||
broadcastDispatcher = fakeBroadcastDispatcher
|
||||
)
|
||||
|
||||
underTest.initialize()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `demo command flow - returns args`() =
|
||||
testScope.runTest {
|
||||
var latest: Bundle? = null
|
||||
val flow = underTest.demoFlowForCommand(TEST_COMMAND)
|
||||
val job = launch { flow.collect { latest = it } }
|
||||
|
||||
sendDemoCommand(args = mapOf("key1" to "val1"))
|
||||
assertThat(latest!!.getString("key1")).isEqualTo("val1")
|
||||
|
||||
sendDemoCommand(args = mapOf("key2" to "val2"))
|
||||
assertThat(latest!!.getString("key2")).isEqualTo("val2")
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private fun sendDemoCommand(command: String? = TEST_COMMAND, args: Map<String, String>) {
|
||||
val intent = Intent(ACTION_DEMO)
|
||||
intent.putExtra("command", command)
|
||||
args.forEach { arg -> intent.putExtra(arg.key, arg.value) }
|
||||
|
||||
fakeBroadcastDispatcher.registeredReceivers.forEach { it.onReceive(context, intent) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Use a valid command until we properly fake out everything
|
||||
const val TEST_COMMAND = COMMAND_STATUS
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user