Loading packages/SystemUI/src/com/android/systemui/media/MediaData.kt +2 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,8 @@ data class MediaData( val packageName: String?, val token: MediaSession.Token?, val clickIntent: PendingIntent?, val device: MediaDeviceData? val device: MediaDeviceData?, val notificationKey: String = "INVALID" ) /** State of a media action. */ Loading packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +21 −1 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ import com.android.internal.graphics.ColorUtils import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.notification.MediaNotificationProcessor import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.util.Assert import com.android.systemui.util.Utils Loading Loading @@ -77,6 +79,8 @@ fun isMediaNotification(sbn: StatusBarNotification): Boolean { class MediaDataManager @Inject constructor( private val context: Context, private val mediaControllerFactory: MediaControllerFactory, private val mediaTimeoutListener: MediaTimeoutListener, private val notificationEntryManager: NotificationEntryManager, @Background private val backgroundExecutor: Executor, @Main private val foregroundExecutor: Executor ) { Loading @@ -84,6 +88,12 @@ class MediaDataManager @Inject constructor( private val listeners: MutableSet<Listener> = mutableSetOf() private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() init { mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean -> setTimedOut(token, timedOut) } addListener(mediaTimeoutListener) } fun onNotificationAdded(key: String, sbn: StatusBarNotification) { if (Utils.useQsMediaPlayer(context) && isMediaNotification(sbn)) { Assert.isMainThread() Loading Loading @@ -112,6 +122,16 @@ class MediaDataManager @Inject constructor( */ fun removeListener(listener: Listener) = listeners.remove(listener) private fun setTimedOut(token: String, timedOut: Boolean) { if (!timedOut) { return } mediaEntries[token]?.let { notificationEntryManager.removeNotification(it.notificationKey, null /* ranking */, UNDEFINED_DISMISS_REASON) } } private fun loadMediaDataInBg(key: String, sbn: StatusBarNotification) { val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) as MediaSession.Token? Loading Loading @@ -223,7 +243,7 @@ class MediaDataManager @Inject constructor( foregroundExecutor.execute { onMediaDataLoaded(key, MediaData(true, bgColor, app, smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null)) notif.contentIntent, null, key)) } } Loading packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.media import android.media.session.MediaController import android.media.session.PlaybackState import android.os.SystemProperties import android.util.Log import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState import com.android.systemui.util.concurrency.DelayableExecutor import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton private const val DEBUG = true private const val TAG = "MediaTimeout" private val PAUSED_MEDIA_TIMEOUT = SystemProperties .getLong("debug.sysui.media_timeout", TimeUnit.MINUTES.toMillis(10)) /** * Controller responsible for keeping track of playback states and expiring inactive streams. */ @Singleton class MediaTimeoutListener @Inject constructor( private val mediaControllerFactory: MediaControllerFactory, @Main private val mainExecutor: DelayableExecutor ) : MediaDataManager.Listener { private val mediaListeners: MutableMap<String, PlaybackStateListener> = mutableMapOf() lateinit var timeoutCallback: (String, Boolean) -> Unit override fun onMediaDataLoaded(key: String, data: MediaData) { if (mediaListeners.containsKey(key)) { return } mediaListeners[key] = PlaybackStateListener(key, data) } override fun onMediaDataRemoved(key: String) { mediaListeners.remove(key)?.destroy() } fun isTimedOut(key: String): Boolean { return mediaListeners[key]?.timedOut ?: false } private inner class PlaybackStateListener( private val key: String, data: MediaData ) : MediaController.Callback() { var timedOut = false private val mediaController = mediaControllerFactory.create(data.token) private var cancellation: Runnable? = null init { mediaController.registerCallback(this) } fun destroy() { mediaController.unregisterCallback(this) } override fun onPlaybackStateChanged(state: PlaybackState?) { if (DEBUG) { Log.v(TAG, "onPlaybackStateChanged: $state") } expireMediaTimeout(key, "playback state ativity - $state, $key") if (state == null || !isPlayingState(state.state)) { if (DEBUG) { Log.v(TAG, "schedule timeout for $key") } expireMediaTimeout(key, "PLAYBACK STATE CHANGED - $state") cancellation = mainExecutor.executeDelayed({ cancellation = null if (DEBUG) { Log.v(TAG, "Execute timeout for $key") } timedOut = true timeoutCallback(key, timedOut) }, PAUSED_MEDIA_TIMEOUT) } else { timedOut = false timeoutCallback(key, timedOut) } } private fun expireMediaTimeout(mediaNotificationKey: String, reason: String) { cancellation?.apply { if (DEBUG) { Log.v(TAG, "media timeout cancelled for $mediaNotificationKey, reason: $reason") } run() } cancellation = null } } } No newline at end of file packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +0 −42 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui.statusbar; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK; import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER; import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; Loading @@ -36,7 +35,6 @@ import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.AsyncTask; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; Loading Loading @@ -80,7 +78,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import dagger.Lazy; Loading @@ -91,8 +88,6 @@ import dagger.Lazy; public class NotificationMediaManager implements Dumpable { private static final String TAG = "NotificationMediaManager"; public static final boolean DEBUG_MEDIA = false; private static final long PAUSED_MEDIA_TIMEOUT = SystemProperties .getLong("debug.sysui.media_timeout", TimeUnit.MINUTES.toMillis(10)); private final StatusBarStateController mStatusBarStateController = Dependency.get(StatusBarStateController.class); Loading Loading @@ -134,7 +129,6 @@ public class NotificationMediaManager implements Dumpable { private MediaController mMediaController; private String mMediaNotificationKey; private MediaMetadata mMediaMetadata; private Runnable mMediaTimeoutCancellation; private BackDropView mBackdrop; private ImageView mBackdropFront; Loading Loading @@ -164,47 +158,11 @@ public class NotificationMediaManager implements Dumpable { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state); } if (mMediaTimeoutCancellation != null) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: media timeout cancelled"); } mMediaTimeoutCancellation.run(); mMediaTimeoutCancellation = null; } if (state != null) { if (!isPlaybackActive(state.getState())) { clearCurrentMediaNotification(); } findAndUpdateMediaNotifications(); scheduleMediaTimeout(state); } } private void scheduleMediaTimeout(PlaybackState state) { final NotificationEntry entry; synchronized (mEntryManager) { entry = mEntryManager.getActiveNotificationUnfiltered(mMediaNotificationKey); } if (entry != null) { if (!isPlayingState(state.getState())) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: schedule timeout for " + mMediaNotificationKey); } mMediaTimeoutCancellation = mMainExecutor.executeDelayed(() -> { synchronized (mEntryManager) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: execute timeout for " + mMediaNotificationKey); } if (mMediaNotificationKey == null) { return; } mEntryManager.removeNotification(mMediaNotificationKey, null, UNDEFINED_DISMISS_REASON); } }, PAUSED_MEDIA_TIMEOUT); } } } Loading packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { mManager.addListener(mListener); mMediaData = new MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null); new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, KEY); mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME); } Loading Loading
packages/SystemUI/src/com/android/systemui/media/MediaData.kt +2 −1 Original line number Diff line number Diff line Loading @@ -35,7 +35,8 @@ data class MediaData( val packageName: String?, val token: MediaSession.Token?, val clickIntent: PendingIntent?, val device: MediaDeviceData? val device: MediaDeviceData?, val notificationKey: String = "INVALID" ) /** State of a media action. */ Loading
packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +21 −1 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ import com.android.internal.graphics.ColorUtils import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.notification.MediaNotificationProcessor import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON import com.android.systemui.statusbar.notification.row.HybridGroupManager import com.android.systemui.util.Assert import com.android.systemui.util.Utils Loading Loading @@ -77,6 +79,8 @@ fun isMediaNotification(sbn: StatusBarNotification): Boolean { class MediaDataManager @Inject constructor( private val context: Context, private val mediaControllerFactory: MediaControllerFactory, private val mediaTimeoutListener: MediaTimeoutListener, private val notificationEntryManager: NotificationEntryManager, @Background private val backgroundExecutor: Executor, @Main private val foregroundExecutor: Executor ) { Loading @@ -84,6 +88,12 @@ class MediaDataManager @Inject constructor( private val listeners: MutableSet<Listener> = mutableSetOf() private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() init { mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean -> setTimedOut(token, timedOut) } addListener(mediaTimeoutListener) } fun onNotificationAdded(key: String, sbn: StatusBarNotification) { if (Utils.useQsMediaPlayer(context) && isMediaNotification(sbn)) { Assert.isMainThread() Loading Loading @@ -112,6 +122,16 @@ class MediaDataManager @Inject constructor( */ fun removeListener(listener: Listener) = listeners.remove(listener) private fun setTimedOut(token: String, timedOut: Boolean) { if (!timedOut) { return } mediaEntries[token]?.let { notificationEntryManager.removeNotification(it.notificationKey, null /* ranking */, UNDEFINED_DISMISS_REASON) } } private fun loadMediaDataInBg(key: String, sbn: StatusBarNotification) { val token = sbn.notification.extras.getParcelable(Notification.EXTRA_MEDIA_SESSION) as MediaSession.Token? Loading Loading @@ -223,7 +243,7 @@ class MediaDataManager @Inject constructor( foregroundExecutor.execute { onMediaDataLoaded(key, MediaData(true, bgColor, app, smallIconDrawable, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null)) notif.contentIntent, null, key)) } } Loading
packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.media import android.media.session.MediaController import android.media.session.PlaybackState import android.os.SystemProperties import android.util.Log import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState import com.android.systemui.util.concurrency.DelayableExecutor import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Singleton private const val DEBUG = true private const val TAG = "MediaTimeout" private val PAUSED_MEDIA_TIMEOUT = SystemProperties .getLong("debug.sysui.media_timeout", TimeUnit.MINUTES.toMillis(10)) /** * Controller responsible for keeping track of playback states and expiring inactive streams. */ @Singleton class MediaTimeoutListener @Inject constructor( private val mediaControllerFactory: MediaControllerFactory, @Main private val mainExecutor: DelayableExecutor ) : MediaDataManager.Listener { private val mediaListeners: MutableMap<String, PlaybackStateListener> = mutableMapOf() lateinit var timeoutCallback: (String, Boolean) -> Unit override fun onMediaDataLoaded(key: String, data: MediaData) { if (mediaListeners.containsKey(key)) { return } mediaListeners[key] = PlaybackStateListener(key, data) } override fun onMediaDataRemoved(key: String) { mediaListeners.remove(key)?.destroy() } fun isTimedOut(key: String): Boolean { return mediaListeners[key]?.timedOut ?: false } private inner class PlaybackStateListener( private val key: String, data: MediaData ) : MediaController.Callback() { var timedOut = false private val mediaController = mediaControllerFactory.create(data.token) private var cancellation: Runnable? = null init { mediaController.registerCallback(this) } fun destroy() { mediaController.unregisterCallback(this) } override fun onPlaybackStateChanged(state: PlaybackState?) { if (DEBUG) { Log.v(TAG, "onPlaybackStateChanged: $state") } expireMediaTimeout(key, "playback state ativity - $state, $key") if (state == null || !isPlayingState(state.state)) { if (DEBUG) { Log.v(TAG, "schedule timeout for $key") } expireMediaTimeout(key, "PLAYBACK STATE CHANGED - $state") cancellation = mainExecutor.executeDelayed({ cancellation = null if (DEBUG) { Log.v(TAG, "Execute timeout for $key") } timedOut = true timeoutCallback(key, timedOut) }, PAUSED_MEDIA_TIMEOUT) } else { timedOut = false timeoutCallback(key, timedOut) } } private fun expireMediaTimeout(mediaNotificationKey: String, reason: String) { cancellation?.apply { if (DEBUG) { Log.v(TAG, "media timeout cancelled for $mediaNotificationKey, reason: $reason") } run() } cancellation = null } } } No newline at end of file
packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +0 −42 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.systemui.statusbar; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK; import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER; import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; Loading @@ -36,7 +35,6 @@ import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.AsyncTask; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; Loading Loading @@ -80,7 +78,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import dagger.Lazy; Loading @@ -91,8 +88,6 @@ import dagger.Lazy; public class NotificationMediaManager implements Dumpable { private static final String TAG = "NotificationMediaManager"; public static final boolean DEBUG_MEDIA = false; private static final long PAUSED_MEDIA_TIMEOUT = SystemProperties .getLong("debug.sysui.media_timeout", TimeUnit.MINUTES.toMillis(10)); private final StatusBarStateController mStatusBarStateController = Dependency.get(StatusBarStateController.class); Loading Loading @@ -134,7 +129,6 @@ public class NotificationMediaManager implements Dumpable { private MediaController mMediaController; private String mMediaNotificationKey; private MediaMetadata mMediaMetadata; private Runnable mMediaTimeoutCancellation; private BackDropView mBackdrop; private ImageView mBackdropFront; Loading Loading @@ -164,47 +158,11 @@ public class NotificationMediaManager implements Dumpable { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state); } if (mMediaTimeoutCancellation != null) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: media timeout cancelled"); } mMediaTimeoutCancellation.run(); mMediaTimeoutCancellation = null; } if (state != null) { if (!isPlaybackActive(state.getState())) { clearCurrentMediaNotification(); } findAndUpdateMediaNotifications(); scheduleMediaTimeout(state); } } private void scheduleMediaTimeout(PlaybackState state) { final NotificationEntry entry; synchronized (mEntryManager) { entry = mEntryManager.getActiveNotificationUnfiltered(mMediaNotificationKey); } if (entry != null) { if (!isPlayingState(state.getState())) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: schedule timeout for " + mMediaNotificationKey); } mMediaTimeoutCancellation = mMainExecutor.executeDelayed(() -> { synchronized (mEntryManager) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: execute timeout for " + mMediaNotificationKey); } if (mMediaNotificationKey == null) { return; } mEntryManager.removeNotification(mMediaNotificationKey, null, UNDEFINED_DISMISS_REASON); } }, PAUSED_MEDIA_TIMEOUT); } } } Loading
packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +1 −1 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { mManager.addListener(mListener); mMediaData = new MediaData(true, BG_COLOR, APP, null, ARTIST, TITLE, null, new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null); new ArrayList<>(), new ArrayList<>(), PACKAGE, null, null, null, KEY); mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME); } Loading