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

Commit ed5ced3f authored by Beth Thibodeau's avatar Beth Thibodeau Committed by Android (Google) Code Review
Browse files

Merge "Sort remote cast notifications to the end" into sc-v2-dev

parents 69cd4caa d0a83fe8
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -833,12 +833,15 @@ internal object MediaPlayerData {
    )

    private val comparator =
            compareByDescending<MediaSortKey> { it.data.isPlaying == true && it.data.isLocalSession }
                    .thenByDescending { it.data.isPlaying }
            compareByDescending<MediaSortKey> { it.data.isPlaying == true &&
                        it.data.playbackLocation == MediaData.PLAYBACK_LOCAL }
                .thenByDescending { it.data.isPlaying == true &&
                        it.data.playbackLocation == MediaData.PLAYBACK_CAST_LOCAL }
                .thenByDescending { if (shouldPrioritizeSs) it.isSsMediaRec else !it.isSsMediaRec }
                .thenByDescending { !it.data.resumption }
                .thenByDescending { it.data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE }
                .thenByDescending { it.updateTime }
                    .thenByDescending { !it.data.isLocalSession }
                .thenByDescending { it.data.notificationKey }

    private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator)
    private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf()
+16 −3
Original line number Diff line number Diff line
@@ -82,9 +82,9 @@ data class MediaData(
     */
    var resumeAction: Runnable?,
    /**
     * Local or remote playback
     * Playback location: one of PLAYBACK_LOCAL, PLAYBACK_CAST_LOCAL, or PLAYBACK_CAST_REMOTE
     */
    var isLocalSession: Boolean = true,
    var playbackLocation: Int = PLAYBACK_LOCAL,
    /**
     * Indicates that this player is a resumption player (ie. It only shows a play actions which
     * will start the app and start playing).
@@ -110,7 +110,20 @@ data class MediaData(
     * Timestamp when this player was last active.
     */
    var lastActive: Long = 0L
)
) {
    companion object {
        /** Media is playing on the local device */
        const val PLAYBACK_LOCAL = 0
        /** Media is cast but originated on the local device */
        const val PLAYBACK_CAST_LOCAL = 1
        /** Media is from a remote cast notification */
        const val PLAYBACK_CAST_REMOTE = 2
    }

    fun isLocalSession(): Boolean {
        return playbackLocation == PLAYBACK_LOCAL
    }
}

/** State of a media action. */
data class MediaAction(
+28 −5
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.ImageDecoder
@@ -145,6 +147,24 @@ class MediaDataManager(
    private var smartspaceSession: SmartspaceSession? = null
    private var allowMediaRecommendations = Utils.allowMediaRecommendations(context)

    /**
     * Check whether this notification is an RCN
     * TODO(b/204910409) implement new API for explicitly declaring this
     */
    private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean {
        val pm = context.packageManager
        try {
            val info = pm.getApplicationInfo(sbn.packageName, PackageManager.MATCH_SYSTEM_ONLY)
            if (info.privateFlags and ApplicationInfo.PRIVATE_FLAG_PRIVILEGED != 0) {
                val extras = sbn.notification.extras
                if (extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
                    return true
                }
            }
        } catch (e: PackageManager.NameNotFoundException) { }
        return false
    }

    @Inject
    constructor(
        context: Context,
@@ -434,7 +454,7 @@ class MediaDataManager(
        val existed = mediaEntries[key] != null
        backgroundExecutor.execute {
            mediaEntries[key]?.let { mediaData ->
                if (mediaData.isLocalSession) {
                if (mediaData.isLocalSession()) {
                    mediaData.token?.let {
                        val mediaController = mediaControllerFactory.create(it)
                        mediaController.transportControls.stop()
@@ -618,8 +638,11 @@ class MediaDataManager(
            }
        }

        val isLocalSession = mediaController.playbackInfo?.playbackType ==
            MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL
        val playbackLocation =
                if (isRemoteCastNotification(sbn)) MediaData.PLAYBACK_CAST_REMOTE
                else if (mediaController.playbackInfo?.playbackType ==
                    MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) MediaData.PLAYBACK_LOCAL
                else MediaData.PLAYBACK_CAST_LOCAL
        val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) } ?: null
        val lastActive = systemClock.elapsedRealtime()
        foregroundExecutor.execute {
@@ -629,7 +652,7 @@ class MediaDataManager(
            onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
                    smallIcon, artist, song, artWorkIcon, actionIcons,
                    actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
                    active, resumeAction = resumeAction, isLocalSession = isLocalSession,
                    active, resumeAction = resumeAction, playbackLocation = playbackLocation,
                    notificationKey = key, hasCheckedForResume = hasCheckedForResume,
                    isPlaying = isPlaying, isClearable = sbn.isClearable(),
                    lastActive = lastActive))
@@ -754,7 +777,7 @@ class MediaDataManager(
    fun onNotificationRemoved(key: String) {
        Assert.isMainThread()
        val removed = mediaEntries.remove(key)
        if (useMediaResumption && removed?.resumeAction != null && removed?.isLocalSession) {
        if (useMediaResumption && removed?.resumeAction != null && removed?.isLocalSession()) {
            Log.d(TAG, "Not removing $key because resumable")
            // Move to resume key (aka package name) if that key doesn't already exist.
            val resumeAction = getResumeMediaAction(removed.resumeAction!!)
+1 −1
Original line number Diff line number Diff line
@@ -193,7 +193,7 @@ class MediaResumeListener @Inject constructor(
                mediaBrowser = null
            }
            // If we don't have a resume action, check if we haven't already
            if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession) {
            if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession()) {
                // TODO also check for a media button receiver intended for restarting (b/154127084)
                Log.d(TAG, "Checking for service component for " + data.packageName)
                val pm = context.packageManager
+29 −12
Original line number Diff line number Diff line
@@ -104,37 +104,54 @@ class MediaCarouselControllerTest : SysuiTestCase() {
    fun testPlayerOrdering() {
        // Test values: key, data, last active time
        val playingLocal = Triple("playing local",
            DATA.copy(active = true, isPlaying = true, isLocalSession = true, resumption = false),
            DATA.copy(active = true, isPlaying = true,
                    playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
            4500L)

        val playingRemote = Triple("playing remote",
            DATA.copy(active = true, isPlaying = true, isLocalSession = false, resumption = false),
        val playingCast = Triple("playing cast",
            DATA.copy(active = true, isPlaying = true,
                    playbackLocation = MediaData.PLAYBACK_CAST_LOCAL, resumption = false),
            5000L)

        val pausedLocal = Triple("paused local",
            DATA.copy(active = true, isPlaying = false, isLocalSession = true, resumption = false),
            DATA.copy(active = true, isPlaying = false,
                    playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
            1000L)

        val pausedRemote = Triple("paused remote",
            DATA.copy(active = true, isPlaying = false, isLocalSession = false, resumption = false),
        val pausedCast = Triple("paused cast",
            DATA.copy(active = true, isPlaying = false,
                    playbackLocation = MediaData.PLAYBACK_CAST_LOCAL, resumption = false),
            2000L)

        val playingRcn = Triple("playing RCN",
            DATA.copy(active = true, isPlaying = true,
                    playbackLocation = MediaData.PLAYBACK_CAST_REMOTE, resumption = false),
            5000L)

        val pausedRcn = Triple("paused RCN",
                DATA.copy(active = true, isPlaying = false,
                        playbackLocation = MediaData.PLAYBACK_CAST_REMOTE, resumption = false),
                5000L)

        val resume1 = Triple("resume 1",
            DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
            DATA.copy(active = false, isPlaying = false,
                    playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
            500L)

        val resume2 = Triple("resume 2",
            DATA.copy(active = false, isPlaying = false, isLocalSession = true, resumption = true),
            DATA.copy(active = false, isPlaying = false,
                    playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
            1000L)

        // Expected ordering for media players:
        // Actively playing local sessions
        // Actively playing remote sessions
        // Paused sessions, by last active
        // Actively playing cast sessions
        // Paused local and cast sessions, by last active
        // RCNs
        // Resume controls, by last active

        val expected = listOf(playingLocal, playingRemote, pausedRemote, pausedLocal, resume2,
            resume1)
        val expected = listOf(playingLocal, playingCast, pausedCast, pausedLocal, playingRcn,
                pausedRcn, resume2, resume1)

        expected.forEach {
            clock.setCurrentTimeMillis(it.third)
Loading