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

Commit 473235f3 authored by Michael Mikhail's avatar Michael Mikhail Committed by Android (Google) Code Review
Browse files

Merge "[Media TTT] Handle multiple devices" into tm-qpr-dev

parents 3bd6c98b b69b45c6
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ class MediaTttCommandLineHelper @Inject constructor(
            @SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE
            val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
                    as StatusBarManager
            val routeInfo = MediaRoute2Info.Builder("id", args[0])
            val routeInfo = MediaRoute2Info.Builder(if (args.size >= 4) args[3] else "id", args[0])
                    .addFeature("feature")
            val useAppIcon = !(args.size >= 3 && args[2] == "useAppIcon=false")
            if (useAppIcon) {
@@ -107,7 +107,7 @@ class MediaTttCommandLineHelper @Inject constructor(

        override fun help(pw: PrintWriter) {
            pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND " +
                    "<deviceName> <chipState> useAppIcon=[true|false]")
                    "<deviceName> <chipState> useAppIcon=[true|false] <id>")
        }
    }

@@ -127,8 +127,10 @@ class MediaTttCommandLineHelper @Inject constructor(
            @SuppressLint("WrongConstant") // sysui is allowed to call STATUS_BAR_SERVICE
            val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
                    as StatusBarManager
            val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
                .addFeature("feature")
            val routeInfo = MediaRoute2Info.Builder(
                if (args.size >= 3) args[2] else "id",
                "Test Name"
            ).addFeature("feature")
            val useAppIcon = !(args.size >= 2 && args[1] == "useAppIcon=false")
            if (useAppIcon) {
                routeInfo.setClientPackageName(TEST_PACKAGE_NAME)
@@ -144,7 +146,7 @@ class MediaTttCommandLineHelper @Inject constructor(

        override fun help(pw: PrintWriter) {
            pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND " +
                    "<chipState> useAppIcon=[true|false]")
                    "<chipState> useAppIcon=[true|false] <id>")
        }
    }

+18 −3
Original line number Diff line number Diff line
@@ -121,18 +121,32 @@ class MediaTttChipControllerReceiver @Inject constructor(
        uiEventLogger.logReceiverStateChange(chipState)

        if (chipState == ChipStateReceiver.FAR_FROM_SENDER) {
            removeView(removalReason = ChipStateReceiver.FAR_FROM_SENDER.name)
            removeView(routeInfo.id, removalReason = ChipStateReceiver.FAR_FROM_SENDER.name)
            return
        }
        if (appIcon == null) {
            displayView(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
            displayView(
                ChipReceiverInfo(
                    routeInfo,
                    appIconDrawableOverride = null,
                    appName,
                    id = routeInfo.id,
                )
            )
            return
        }

        appIcon.loadDrawableAsync(
                context,
                Icon.OnDrawableLoadedListener { drawable ->
                    displayView(ChipReceiverInfo(routeInfo, drawable, appName))
                    displayView(
                        ChipReceiverInfo(
                            routeInfo,
                            drawable,
                            appName,
                            id = routeInfo.id,
                        )
                    )
                },
                // Notify the listener on the main handler since the listener will update
                // the UI.
@@ -234,4 +248,5 @@ data class ChipReceiverInfo(
    val appNameOverride: CharSequence?,
    override val windowTitle: String = MediaTttUtils.WINDOW_TITLE_RECEIVER,
    override val wakeReason: String = MediaTttUtils.WAKE_REASON_RECEIVER,
    override val id: String,
) : TemporaryViewInfo()
+2 −1
Original line number Diff line number Diff line
@@ -108,7 +108,7 @@ constructor(
            }

            displayedState = null
            chipbarCoordinator.removeView(removalReason)
            chipbarCoordinator.removeView(routeInfo.id, removalReason)
        } else {
            displayedState = chipState
            chipbarCoordinator.displayView(
@@ -162,6 +162,7 @@ constructor(
            windowTitle = MediaTttUtils.WINDOW_TITLE_SENDER,
            wakeReason = MediaTttUtils.WAKE_REASON_SENDER,
            timeoutMs = chipStateSender.timeout,
            id = routeInfo.id,
        )
    }

+69 −9
Original line number Diff line number Diff line
@@ -93,6 +93,13 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
    /** A string that keeps track of wakelock reason once it is acquired till it gets released */
    private var wakeReasonAcquired: String? = null

    /**
     * A stack of pairs of device id and temporary view info. This is used when there may be
     * multiple devices in range, and we want to always display the chip for the most recently
     * active device.
     */
    internal val activeViews: ArrayDeque<Pair<String, T>> = ArrayDeque()

    /**
     * Displays the view with the provided [newInfo].
     *
@@ -102,6 +109,12 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
    fun displayView(newInfo: T) {
        val currentDisplayInfo = displayInfo

        // Update our list of active devices by removing it if necessary, then adding back at the
        // front of the list
        val id = newInfo.id
        val position = findAndRemoveFromActiveViewsList(id)
        activeViews.addFirst(Pair(id, newInfo))

        if (currentDisplayInfo != null &&
            currentDisplayInfo.info.windowTitle == newInfo.windowTitle) {
            // We're already displaying information in the correctly-titled window, so we just need
@@ -113,7 +126,10 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
                // We're already displaying information but that information is under a different
                // window title. So, we need to remove the old window with the old title and add a
                // new window with the new title.
                removeView(removalReason = "New info has new window title: ${newInfo.windowTitle}")
                removeView(
                    id,
                    removalReason = "New info has new window title: ${newInfo.windowTitle}"
                )
            }

            // At this point, we're guaranteed to no longer be displaying a view.
@@ -140,7 +156,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
            }
            wakeLock?.acquire(newInfo.wakeReason)
            wakeReasonAcquired = newInfo.wakeReason
            logger.logViewAddition(newInfo.windowTitle)
            logger.logViewAddition(id, newInfo.windowTitle)
            inflateAndUpdateView(newInfo)
        }

@@ -151,9 +167,13 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
            // include it just to be safe.
            FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
       )

        // Only cancel timeout of the most recent view displayed, as it will be reset.
        if (position == 0) {
            cancelViewTimeout?.run()
        }
        cancelViewTimeout = mainExecutor.executeDelayed(
            { removeView(REMOVAL_REASON_TIMEOUT) },
            { removeView(id, REMOVAL_REASON_TIMEOUT) },
            timeout.toLong()
        )
    }
@@ -196,28 +216,67 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
    }

    /**
     * Hides the view.
     * Hides the view given its [id].
     *
     * @param id the id of the device responsible of displaying the temp view.
     * @param removalReason a short string describing why the view was removed (timeout, state
     *     change, etc.)
     */
    fun removeView(removalReason: String) {
    fun removeView(id: String, removalReason: String) {
        val currentDisplayInfo = displayInfo ?: return

        val removalPosition = findAndRemoveFromActiveViewsList(id)
        if (removalPosition == null) {
            logger.logViewRemovalIgnored(id, "view not found in the list")
            return
        }
        if (removalPosition != 0) {
            logger.logViewRemovalIgnored(id, "most recent view is being displayed.")
            return
        }
        logger.logViewRemoval(id, removalReason)

        val newViewToDisplay = if (activeViews.isEmpty()) {
            null
        } else {
            activeViews[0].second
        }

        val currentView = currentDisplayInfo.view
        animateViewOut(currentView) {
            windowManager.removeView(currentView)
            wakeLock?.release(wakeReasonAcquired)
        }

        logger.logViewRemoval(removalReason)
        configurationController.removeCallback(displayScaleListener)
        // Re-set to null immediately (instead as part of the animation end runnable) so
        // that if a new view event comes in while this view is animating out, we still display the
        // new view appropriately.
        // that if a new view event comes in while this view is animating out, we still display
        // the new view appropriately.
        displayInfo = null
        // No need to time the view out since it's already gone
        cancelViewTimeout?.run()

        if (newViewToDisplay != null) {
            mainExecutor.executeDelayed({ displayView(newViewToDisplay)}, DISPLAY_VIEW_DELAY)
        }
    }

    /**
     * Finds and removes the active view with the given [id] from the stack, or null if there is no
     * active view with that ID
     *
     * @param id that temporary view belonged to.
     *
     * @return index of the view in the stack , otherwise null.
     */
    private fun findAndRemoveFromActiveViewsList(id: String): Int? {
        for (i in 0 until activeViews.size) {
            if (activeViews[i].first == id) {
                activeViews.removeAt(i)
                return i
            }
        }
        return null
    }

    /**
@@ -258,6 +317,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
}

private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
const val DISPLAY_VIEW_DELAY = 50L

private data class IconInfo(
    val iconName: String,
+5 −0
Original line number Diff line number Diff line
@@ -37,6 +37,11 @@ abstract class TemporaryViewInfo {
     * disappears.
     */
    open val timeoutMs: Int = DEFAULT_TIMEOUT_MILLIS

    /**
     * The id of the temporary view.
     */
    abstract val id: String
}

const val DEFAULT_TIMEOUT_MILLIS = 10000
Loading