Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c65b0f05 authored by Evan Laird's avatar Evan Laird Committed by Android (Google) Code Review
Browse files

Merge changes from topic "demo-mode-flow" into tm-qpr-dev

* changes:
  [Sb refactor] Wifi demo mode
  [Sb refactor] Add a demo command flow method
parents 5bba501a a3cef0ce
Loading
Loading
Loading
Loading
+9 −7
Original line number Diff line number Diff line
@@ -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())) {
+108 −78
Original line number Diff line number Diff line
@@ -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,24 +212,22 @@ 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) {
    private val tracker =
        object : DemoModeAvailabilityTracker(context, globalSettings) {
            override fun onDemoModeAvailabilityChanged() {
                setIsDemoModeAllowed(isDemoModeAvailable)
            }
@@ -218,7 +245,8 @@ class DemoModeController constructor(
            }
        }

    private val broadcastReceiver = object : BroadcastReceiver() {
    private val broadcastReceiver =
        object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                if (DEBUG) {
                    Log.v(TAG, "onReceive: $intent")
@@ -230,8 +258,8 @@ class DemoModeController constructor(
                }

                val bundle = intent.extras ?: return
            val command = bundle.getString("command", "").trim().toLowerCase()
            if (command.length == 0) {
                val command = bundle.getString("command", "").trim().lowercase()
                if (command.isEmpty()) {
                    return
                }

@@ -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
+9 −2
Original line number Diff line number Diff line
@@ -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;
    }
+1 −1
Original line number Diff line number Diff line
@@ -1302,7 +1302,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
            }
        }
        String wifi = args.getString("wifi");
        if (wifi != null) {
        if (wifi != null && !mStatusBarPipelineFlags.runNewWifiIconBackend()) {
            boolean show = wifi.equals("show");
            String level = args.getString("level");
            if (level != null) {
+43 −4
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@ import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconStat
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView;
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel;

import java.util.ArrayList;
import java.util.List;
@@ -56,6 +58,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
    private final int mIconSize;

    private StatusBarWifiView mWifiView;
    private ModernStatusBarWifiView mModernWifiView;
    private boolean mDemoMode;
    private int mColor;

@@ -236,14 +239,14 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da

    public void addDemoWifiView(WifiIconState state) {
        Log.d(TAG, "addDemoWifiView: ");
        // TODO(b/238425913): Migrate this view to {@code ModernStatusBarWifiView}.
        StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, state.slot);

        int viewIndex = getChildCount();
        // If we have mobile views, put wifi before them
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child instanceof StatusBarMobileView) {
            if (child instanceof StatusBarMobileView
                    || child instanceof ModernStatusBarMobileView) {
                viewIndex = i;
                break;
            }
@@ -298,6 +301,30 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
        addView(view, getChildCount(), createLayoutParams());
    }

    /**
     * Add a {@link ModernStatusBarWifiView}
     */
    public void addModernWifiView(LocationBasedWifiViewModel viewModel) {
        Log.d(TAG, "addModernDemoWifiView: ");
        ModernStatusBarWifiView view = ModernStatusBarWifiView
                .constructAndBind(mContext, "wifi", viewModel);

        int viewIndex = getChildCount();
        // If we have mobile views, put wifi before them
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child instanceof StatusBarMobileView
                    || child instanceof ModernStatusBarMobileView) {
                viewIndex = i;
                break;
            }
        }

        mModernWifiView = view;
        mModernWifiView.setStaticDrawableColor(mColor);
        addView(view, viewIndex, createLayoutParams());
    }

    /**
     * Apply an update to a mobile icon view for the given {@link MobileIconState}. For
     * compatibility with {@link MobileContextProvider}, we have to recreate the view every time we
@@ -320,8 +347,14 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da

    public void onRemoveIcon(StatusIconDisplayable view) {
        if (view.getSlot().equals("wifi")) {
            if (view instanceof StatusBarWifiView) {
                removeView(mWifiView);
                mWifiView = null;
            } else if (view instanceof ModernStatusBarWifiView) {
                Log.d(TAG, "onRemoveIcon: removing modern wifi view");
                removeView(mModernWifiView);
                mModernWifiView = null;
            }
        } else if (view instanceof StatusBarMobileView) {
            StatusBarMobileView mobileView = matchingMobileView(view);
            if (mobileView != null) {
@@ -374,8 +407,14 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
        if (mWifiView != null) {
            mWifiView.onDarkChanged(areas, darkIntensity, tint);
        }
        if (mModernWifiView != null) {
            mModernWifiView.onDarkChanged(areas, darkIntensity, tint);
        }
        for (StatusBarMobileView view : mMobileViews) {
            view.onDarkChanged(areas, darkIntensity, tint);
        }
        for (ModernStatusBarMobileView view : mModernMobileViews) {
            view.onDarkChanged(areas, darkIntensity, tint);
        }
    }
}
Loading