[DO NOT MERGE] Support toggling resumption in Settings
Moved setting so we can use Tunable:
adb shell settings put secure qs_media_resumption 1
If setting is changed, removes all existing resume players
Bug: 154039093
Test: manual
Test: atest SettingsProviderTest
Test: atest com.android.systemui.media
Change-Id: Iad056fbad4520cfe762d9e9f5ed62d38ea1117b1
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1fafbd3..4abb2c59 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1898,6 +1898,15 @@
"android.settings.ACTION_DEVICE_CONTROLS_SETTINGS";
/**
+ * Activity Action: Show media control settings
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MEDIA_CONTROLS_SETTINGS =
+ "android.settings.ACTION_MEDIA_CONTROLS_SETTINGS";
+
+ /**
* Activity Action: Show a dialog with disabled by policy message.
* <p> If an user action is disabled by policy, this dialog can be triggered to let
* the user know about this.
@@ -8911,6 +8920,15 @@
public static final String PEOPLE_STRIP = "people_strip";
/**
+ * Whether or not to enable media resumption
+ * When enabled, media controls in quick settings will populate on boot and persist if
+ * resumable via a MediaBrowserService.
+ * @see Settings.Global#SHOW_MEDIA_ON_QUICK_SETTINGS
+ * @hide
+ */
+ public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption";
+
+ /**
* Controls if window magnification is enabled.
* @hide
*/
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 997829e..69b32c2 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2678,4 +2678,9 @@
// CATEGORY: SETTINGS
// OS: R
DEVICE_CONTROLS_SETTINGS = 1844;
+
+ // OPEN: Settings > Sound > Media
+ // CATEGORY: SETTINGS
+ // OS: R
+ MEDIA_CONTROLS_SETTINGS = 1845;
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index c04a1ba..d05e6e1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -164,7 +164,8 @@
Settings.Secure.AWARE_TAP_PAUSE_GESTURE_COUNT,
Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT,
Settings.Secure.PEOPLE_STRIP,
+ Settings.Secure.MEDIA_CONTROLS_RESUME,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 76746e5..fa810bd 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -243,6 +243,7 @@
VALIDATORS.put(Secure.DISPLAY_DENSITY_FORCED, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.PEOPLE_STRIP, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
new InclusiveIntegerRangeValidator(
Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b95af26..b5a2435 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2784,10 +2784,16 @@
recommended controls [CHAR_LIMIT=60] -->
<string name="controls_seeding_in_progress">Loading recommendations</string>
- <!-- Close the controls associated with a specific media session [CHAR_LIMIT=NONE] -->
- <string name="controls_media_close_session">Close this media session</string>
+ <!-- Title for media controls [CHAR_LIMIT=50] -->
+ <string name="controls_media_title">Media</string>
+ <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_close_session">Hide the current session.</string>
+ <!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
+ <string name="controls_media_dismiss_button">Hide</string>
<!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
<string name="controls_media_resume">Resume</string>
+ <!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
+ <string name="controls_media_settings_button">Settings</string>
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
<string name="controls_error_timeout">Inactive, check app</string>
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 1ec285a..c59a548 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -44,7 +44,6 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.notification.MediaNotificationProcessor
import com.android.systemui.statusbar.notification.row.HybridGroupManager
import com.android.systemui.util.Assert
@@ -96,7 +95,7 @@
dumpManager: DumpManager,
mediaTimeoutListener: MediaTimeoutListener,
mediaResumeListener: MediaResumeListener,
- private val useMediaResumption: Boolean,
+ private var useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean
) : Dumpable {
@@ -149,18 +148,9 @@
mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean ->
setTimedOut(token, timedOut) }
addListener(mediaTimeoutListener)
- if (useMediaResumption) {
- mediaResumeListener.addTrackToResumeCallback = { desc: MediaDescription,
- resumeAction: Runnable, token: MediaSession.Token, appName: String,
- appIntent: PendingIntent, packageName: String ->
- addResumptionControls(desc, resumeAction, token, appName, appIntent, packageName)
- }
- mediaResumeListener.resumeComponentFoundCallback = { key: String, action: Runnable? ->
- mediaEntries.get(key)?.resumeAction = action
- mediaEntries.get(key)?.hasCheckedForResume = true
- }
- addListener(mediaResumeListener)
- }
+
+ mediaResumeListener.setManager(this)
+ addListener(mediaResumeListener)
val userFilter = IntentFilter(Intent.ACTION_USER_SWITCHED)
broadcastDispatcher.registerReceiver(userChangeReceiver, userFilter, null, UserHandle.ALL)
@@ -223,7 +213,14 @@
}
}
- private fun addResumptionControls(
+ fun setResumeAction(key: String, action: Runnable?) {
+ mediaEntries.get(key)?.let {
+ it.resumeAction = action
+ it.hasCheckedForResume = true
+ }
+ }
+
+ fun addResumptionControls(
desc: MediaDescription,
action: Runnable,
token: MediaSession.Token,
@@ -233,7 +230,8 @@
) {
// Resume controls don't have a notification key, so store by package name instead
if (!mediaEntries.containsKey(packageName)) {
- val resumeData = LOADING.copy(packageName = packageName, resumeAction = action)
+ val resumeData = LOADING.copy(packageName = packageName, resumeAction = action,
+ hasCheckedForResume = true)
mediaEntries.put(packageName, resumeData)
}
backgroundExecutor.execute {
@@ -301,6 +299,8 @@
) {
if (TextUtils.isEmpty(desc.title)) {
Log.e(TAG, "Description incomplete")
+ // Delete the placeholder entry
+ mediaEntries.remove(packageName)
return
}
@@ -323,7 +323,8 @@
onMediaDataLoaded(packageName, null, MediaData(true, bgColor, appName,
null, desc.subtitle, desc.title, artworkIcon, listOf(mediaAction), listOf(0),
packageName, token, appIntent, device = null, active = false,
- resumeAction = resumeAction))
+ resumeAction = resumeAction, notificationKey = packageName,
+ hasCheckedForResume = true))
}
}
@@ -432,11 +433,12 @@
}
val resumeAction: Runnable? = mediaEntries.get(key)?.resumeAction
+ val hasCheckedForResume = mediaEntries.get(key)?.hasCheckedForResume == true
foregroundExecutor.execute {
onMediaDataLoaded(key, oldKey, MediaData(true, bgColor, app, smallIconDrawable, artist,
song, artWorkIcon, actionIcons, actionsToShowCollapsed, sbn.packageName, token,
notif.contentIntent, null, active = true, resumeAction = resumeAction,
- notificationKey = key))
+ notificationKey = key, hasCheckedForResume = hasCheckedForResume))
}
}
@@ -540,7 +542,7 @@
val data = mediaEntries.remove(key)!!
val resumeAction = getResumeMediaAction(data.resumeAction!!)
val updated = data.copy(token = null, actions = listOf(resumeAction),
- actionsToShowInCompact = listOf(0))
+ actionsToShowInCompact = listOf(0), active = false)
mediaEntries.put(data.packageName, updated)
// Notify listeners of "new" controls
val listenersCopy = listeners.toSet()
@@ -563,20 +565,33 @@
*/
fun hasActiveMedia() = mediaEntries.any { it.value.active }
- fun isActive(token: MediaSession.Token?): Boolean {
- if (token == null) {
- return false
- }
- val controller = mediaControllerFactory.create(token)
- val state = controller?.playbackState?.state
- return state != null && NotificationMediaManager.isActiveState(state)
- }
-
/**
- * Are there any media entries, including resume controls?
+ * Are there any media entries we should display?
+ * If resumption is enabled, this will include inactive players
+ * If resumption is disabled, we only want to show active players
*/
fun hasAnyMedia() = if (useMediaResumption) mediaEntries.isNotEmpty() else hasActiveMedia()
+ fun setMediaResumptionEnabled(isEnabled: Boolean) {
+ if (useMediaResumption == isEnabled) {
+ return
+ }
+
+ useMediaResumption = isEnabled
+
+ if (!useMediaResumption) {
+ // Remove any existing resume controls
+ val listenersCopy = listeners.toSet()
+ val filtered = mediaEntries.filter { !it.value.active }
+ filtered.forEach {
+ mediaEntries.remove(it.key)
+ listenersCopy.forEach { listener ->
+ listener.onMediaDataRemoved(it.key)
+ }
+ }
+ }
+ }
+
interface Listener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index e8a4b1e..0cc1e7b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media
-import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
@@ -25,12 +24,13 @@
import android.content.pm.PackageManager
import android.media.MediaDescription
import android.media.session.MediaController
-import android.media.session.MediaSession
import android.os.UserHandle
+import android.provider.Settings
import android.service.media.MediaBrowserService
import android.util.Log
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.tuner.TunerService
import com.android.systemui.util.Utils
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.Executor
@@ -46,21 +46,14 @@
class MediaResumeListener @Inject constructor(
private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher,
- @Background private val backgroundExecutor: Executor
+ @Background private val backgroundExecutor: Executor,
+ private val tunerService: TunerService
) : MediaDataManager.Listener {
- private val useMediaResumption: Boolean = Utils.useMediaResumption(context)
+ private var useMediaResumption: Boolean = Utils.useMediaResumption(context)
private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue()
- lateinit var addTrackToResumeCallback: (
- MediaDescription,
- Runnable,
- MediaSession.Token,
- String,
- PendingIntent,
- String
- ) -> Unit
- lateinit var resumeComponentFoundCallback: (String, Runnable?) -> Unit
+ private lateinit var mediaDataManager: MediaDataManager
private var mediaBrowser: ResumeMediaBrowser? = null
private var currentUserId: Int
@@ -96,8 +89,8 @@
}
Log.d(TAG, "Adding resume controls $desc")
- addTrackToResumeCallback(desc, resumeAction, token, appName.toString(), appIntent,
- component.packageName)
+ mediaDataManager.addResumptionControls(desc, resumeAction, token, appName.toString(),
+ appIntent, component.packageName)
}
}
@@ -113,6 +106,18 @@
}
}
+ fun setManager(manager: MediaDataManager) {
+ mediaDataManager = manager
+
+ // Add listener for resumption setting changes
+ tunerService.addTunable(object : TunerService.Tunable {
+ override fun onTuningChanged(key: String?, newValue: String?) {
+ useMediaResumption = Utils.useMediaResumption(context)
+ mediaDataManager.setMediaResumptionEnabled(useMediaResumption)
+ }
+ }, Settings.Secure.MEDIA_CONTROLS_RESUME)
+ }
+
private fun loadSavedComponents() {
// Make sure list is empty (if we switched users)
resumeComponents.clear()
@@ -165,7 +170,7 @@
}
} else {
// No service found
- resumeComponentFoundCallback(key, null)
+ mediaDataManager.setResumeAction(key, null)
}
}
}
@@ -182,7 +187,7 @@
object : ResumeMediaBrowser.Callback() {
override fun onConnected() {
Log.d(TAG, "yes we can resume with $componentName")
- resumeComponentFoundCallback(key, getResumeAction(componentName))
+ mediaDataManager.setResumeAction(key, getResumeAction(componentName))
updateResumptionList(componentName)
mediaBrowser?.disconnect()
mediaBrowser = null
@@ -190,7 +195,7 @@
override fun onError() {
Log.e(TAG, "Cannot resume with $componentName")
- resumeComponentFoundCallback(key, null)
+ mediaDataManager.setResumeAction(key, null)
mediaBrowser?.disconnect()
mediaBrowser = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
index 6462f07..1842564 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
@@ -30,8 +30,6 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.systemui.util.Utils;
-
import java.util.List;
/**
@@ -46,7 +44,6 @@
public static final String DELIMITER = ":";
private static final String TAG = "ResumeMediaBrowser";
- private boolean mIsEnabled = false;
private final Context mContext;
private final Callback mCallback;
private MediaBrowser mMediaBrowser;
@@ -59,7 +56,6 @@
* @param componentName Component name of the MediaBrowserService this browser will connect to
*/
public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName) {
- mIsEnabled = Utils.useMediaResumption(context);
mContext = context;
mCallback = callback;
mComponentName = componentName;
@@ -74,9 +70,6 @@
* ResumeMediaBrowser#disconnect will be called automatically with this function.
*/
public void findRecentMedia() {
- if (!mIsEnabled) {
- return;
- }
Log.d(TAG, "Connecting to " + mComponentName);
disconnect();
Bundle rootHints = new Bundle();
@@ -186,9 +179,6 @@
* ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed.
*/
public void restart() {
- if (!mIsEnabled) {
- return;
- }
disconnect();
Bundle rootHints = new Bundle();
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
@@ -250,9 +240,6 @@
* ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed.
*/
public void testConnection() {
- if (!mIsEnabled) {
- return;
- }
disconnect();
final MediaBrowser.ConnectionCallback connectionCallback =
new MediaBrowser.ConnectionCallback() {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 248bdc8..9ad2aa2 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -62,7 +62,8 @@
// shouldn't be reset with tuner settings.
private static final String[] RESET_BLACKLIST = new String[] {
QSTileHost.TILES_SETTING,
- Settings.Secure.DOZE_ALWAYS_ON
+ Settings.Secure.DOZE_ALWAYS_ON,
+ Settings.Secure.MEDIA_CONTROLS_RESUME
};
private final Observer mObserver = new Observer();
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 5c9db54..e5f30cf 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -139,7 +139,8 @@
* Off by default, but can be enabled by setting to 1
*/
public static boolean useMediaResumption(Context context) {
- int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_resumption", 0);
+ int flag = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.MEDIA_CONTROLS_RESUME, 1);
return useQsMediaPlayer(context) && flag > 0;
}
}