Merge "Allow user to block individual apps from resuming" into rvc-qpr-dev

This commit is contained in:
Beth Thibodeau
2020-08-18 14:29:56 +00:00
committed by Android (Google) Code Review
8 changed files with 123 additions and 5 deletions

View File

@@ -8928,6 +8928,14 @@ public final class Settings {
*/
public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption";
/**
* Controls which packages are blocked from persisting in media controls when resumption is
* enabled. The list of packages is set by the user in the Settings app.
* @see Settings.Secure#MEDIA_CONTROLS_RESUME
* @hide
*/
public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked";
/**
* Controls if window magnification is enabled.
* @hide

View File

@@ -165,6 +165,7 @@ public class SecureSettings {
Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT,
Settings.Secure.PEOPLE_STRIP,
Settings.Secure.MEDIA_CONTROLS_RESUME,
Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS
};

View File

@@ -244,6 +244,8 @@ public class SecureSettingsValidators {
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.MEDIA_CONTROLS_RESUME_BLOCKED,
COLON_SEPARATED_PACKAGE_LIST_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
new InclusiveIntegerRangeValidator(
Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,

View File

@@ -55,6 +55,8 @@ import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.collections.ArrayList
import kotlin.collections.LinkedHashMap
// URI fields to try loading album art from
private val ART_URIS = arrayOf(
@@ -102,6 +104,17 @@ class MediaDataManager(
private val listeners: MutableSet<Listener> = mutableSetOf()
private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
internal var appsBlockedFromResume: MutableSet<String> = Utils.getBlockedMediaApps(context)
set(value) {
// Update list
appsBlockedFromResume.clear()
appsBlockedFromResume.addAll(value)
// Remove any existing resume players that are now blocked
appsBlockedFromResume.forEach {
removeAllForPackage(it)
}
}
@Inject
constructor(
@@ -523,7 +536,8 @@ class MediaDataManager(
fun onNotificationRemoved(key: String) {
Assert.isMainThread()
val removed = mediaEntries.remove(key)
if (useMediaResumption && removed?.resumeAction != null) {
if (useMediaResumption && removed?.resumeAction != null &&
!isBlockedFromResume(removed?.packageName)) {
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!!)
@@ -560,6 +574,13 @@ class MediaDataManager(
}
}
private fun isBlockedFromResume(packageName: String?): Boolean {
if (packageName == null) {
return true
}
return appsBlockedFromResume.contains(packageName)
}
fun setMediaResumptionEnabled(isEnabled: Boolean) {
if (useMediaResumption == isEnabled) {
return
@@ -602,6 +623,7 @@ class MediaDataManager(
println("listeners: $listeners")
println("mediaEntries: $mediaEntries")
println("useMediaResumption: $useMediaResumption")
println("appsBlockedFromResume: $appsBlockedFromResume")
}
}
}

View File

@@ -52,6 +52,7 @@ class MediaResumeListener @Inject constructor(
private var useMediaResumption: Boolean = Utils.useMediaResumption(context)
private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue()
private var blockedApps: MutableSet<String> = Utils.getBlockedMediaApps(context)
private lateinit var mediaDataManager: MediaDataManager
@@ -114,6 +115,14 @@ class MediaResumeListener @Inject constructor(
mediaDataManager.setMediaResumptionEnabled(useMediaResumption)
}
}, Settings.Secure.MEDIA_CONTROLS_RESUME)
// Listen to changes in which apps are allowed to persist
tunerService.addTunable(object : TunerService.Tunable {
override fun onTuningChanged(key: String?, newValue: String?) {
blockedApps = Utils.getBlockedMediaApps(context)
mediaDataManager.appsBlockedFromResume = blockedApps
}
}, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED)
}
fun isResumptionEnabled() = useMediaResumption
@@ -144,8 +153,10 @@ class MediaResumeListener @Inject constructor(
}
resumeComponents.forEach {
val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it)
browser.findRecentMedia()
if (!blockedApps.contains(it.packageName)) {
val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it)
browser.findRecentMedia()
}
}
}
@@ -154,7 +165,8 @@ class MediaResumeListener @Inject constructor(
// If this had been started from a resume state, disconnect now that it's live
mediaBrowser?.disconnect()
// If we don't have a resume action, check if we haven't already
if (data.resumeAction == null && !data.hasCheckedForResume) {
if (data.resumeAction == null && !data.hasCheckedForResume &&
!blockedApps.contains(data.packageName)) {
// 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

View File

@@ -63,7 +63,8 @@ public class TunerServiceImpl extends TunerService {
private static final String[] RESET_BLACKLIST = new String[] {
QSTileHost.TILES_SETTING,
Settings.Secure.DOZE_ALWAYS_ON,
Settings.Secure.MEDIA_CONTROLS_RESUME
Settings.Secure.MEDIA_CONTROLS_RESUME,
Secure.MEDIA_CONTROLS_RESUME_BLOCKED
};
private final Observer mObserver = new Observer();

View File

@@ -21,12 +21,15 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.View;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
public class Utils {
@@ -141,4 +144,21 @@ public class Utils {
Settings.Secure.MEDIA_CONTROLS_RESUME, 1);
return useQsMediaPlayer(context) && flag > 0;
}
/**
* Get the set of apps for which the user has manually disabled resumption.
*/
public static Set<String> getBlockedMediaApps(Context context) {
String list = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED);
if (TextUtils.isEmpty(list)) {
return new HashSet<>();
}
String[] names = list.split(":");
Set<String> apps = new HashSet<>(names.length);
for (String s : names) {
apps.add(s);
}
return apps;
}
}

View File

@@ -196,6 +196,58 @@ class MediaDataManagerTest : SysuiTestCase() {
assertThat(listener.removedKey!!).isEqualTo(KEY_2)
}
@Test
fun testAppBlockedFromResumption() {
// GIVEN that the manager has a notification with a resume action
val listener = TestListener()
mediaDataManager.addListener(listener)
whenever(controller.metadata).thenReturn(metadataBuilder.build())
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
val data = listener.data!!
assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// and the manager should block the package from creating resume controls
val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app")
mediaDataManager.appsBlockedFromResume = blocked
// WHEN the notification is removed
mediaDataManager.onNotificationRemoved(KEY)
// THEN the media data is removed
assertThat(listener.removedKey!!).isEqualTo(KEY)
}
@Test
fun testAppUnblockedFromResumption() {
// GIVEN that an app was blocked from resuming
val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app")
mediaDataManager.appsBlockedFromResume = blocked
// and GIVEN that the manager has a notification from that app with a resume action
val listener = TestListener()
mediaDataManager.addListener(listener)
whenever(controller.metadata).thenReturn(metadataBuilder.build())
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
val data = listener.data!!
assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
// WHEN the app is unblocked
mediaDataManager.appsBlockedFromResume = mutableSetOf("com.example.app")
// and the notification is removed
mediaDataManager.onNotificationRemoved(KEY)
// THEN the entry will stay as a resume control
assertThat(listener.key!!).isEqualTo(PACKAGE_NAME)
assertThat(listener.oldKey!!).isEqualTo(KEY)
}
@Test
fun testAddResumptionControls() {
val listener = TestListener()