Merge "Controls UI - Add 'reset' option for seeding" into rvc-dev
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index 5d03fc5..7e8fec7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -45,7 +45,7 @@
companion object {
private const val TAG = "ControlsBindingControllerImpl"
private const val MAX_CONTROLS_REQUEST = 100000L
- private const val SUGGESTED_CONTROLS_REQUEST = 4L
+ private const val SUGGESTED_CONTROLS_REQUEST = 6L
}
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
@@ -61,6 +61,11 @@
*/
private var statefulControlSubscriber: StatefulControlSubscriber? = null
+ /*
+ * Will track any active load subscriber. Only one can be active at any time.
+ */
+ private var loadSubscriber: LoadSubscriber? = null
+
private val actionCallbackService = object : IControlsActionCallback.Stub() {
override fun accept(
token: IBinder,
@@ -99,17 +104,24 @@
component: ComponentName,
callback: ControlsBindingController.LoadCallback
): Runnable {
- val subscriber = LoadSubscriber(callback, MAX_CONTROLS_REQUEST)
- retrieveLifecycleManager(component).maybeBindAndLoad(subscriber)
- return subscriber.loadCancel()
+ loadSubscriber?.loadCancel()
+
+ val ls = LoadSubscriber(callback, MAX_CONTROLS_REQUEST)
+ loadSubscriber = ls
+
+ retrieveLifecycleManager(component).maybeBindAndLoad(ls)
+ return ls.loadCancel()
}
override fun bindAndLoadSuggested(
component: ComponentName,
callback: ControlsBindingController.LoadCallback
) {
- val subscriber = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST)
- retrieveLifecycleManager(component).maybeBindAndLoadSuggested(subscriber)
+ loadSubscriber?.loadCancel()
+ val ls = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST)
+ loadSubscriber = ls
+
+ retrieveLifecycleManager(component).maybeBindAndLoadSuggested(ls)
}
override fun subscribe(structureInfo: StructureInfo) {
@@ -152,13 +164,16 @@
override fun changeUser(newUser: UserHandle) {
if (newUser == currentUser) return
- unsubscribe()
unbind()
- currentProvider = null
currentUser = newUser
}
private fun unbind() {
+ unsubscribe()
+
+ loadSubscriber?.loadCancel()
+ loadSubscriber = null
+
currentProvider?.unbindService()
currentProvider = null
}
@@ -210,6 +225,20 @@
val callback: ControlsBindingController.LoadCallback
) : CallbackRunnable(token) {
override fun doRun() {
+ Log.d(TAG, "LoadSubscription: Complete and loading controls")
+ callback.accept(list)
+ }
+ }
+
+ private inner class OnCancelAndLoadRunnable(
+ token: IBinder,
+ val list: List<Control>,
+ val subscription: IControlsSubscription,
+ val callback: ControlsBindingController.LoadCallback
+ ) : CallbackRunnable(token) {
+ override fun doRun() {
+ Log.d(TAG, "LoadSubscription: Canceling and loading controls")
+ provider?.cancelSubscription(subscription)
callback.accept(list)
}
}
@@ -220,6 +249,7 @@
val requestLimit: Long
) : CallbackRunnable(token) {
override fun doRun() {
+ Log.d(TAG, "LoadSubscription: Starting subscription")
provider?.startSubscription(subscription, requestLimit)
}
}
@@ -254,34 +284,54 @@
val requestLimit: Long
) : IControlsSubscriber.Stub() {
val loadedControls = ArrayList<Control>()
- var hasError = false
+ private var isTerminated = false
private var _loadCancelInternal: (() -> Unit)? = null
+ private lateinit var subscription: IControlsSubscription
+
fun loadCancel() = Runnable {
- Log.d(TAG, "Cancel load requested")
- _loadCancelInternal?.invoke()
- }
+ Log.d(TAG, "Cancel load requested")
+ _loadCancelInternal?.invoke()
+ }
override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
- _loadCancelInternal = subs::cancel
+ subscription = subs
+ _loadCancelInternal = { currentProvider?.cancelSubscription(subscription) }
backgroundExecutor.execute(OnSubscribeRunnable(token, subs, requestLimit))
}
override fun onNext(token: IBinder, c: Control) {
- backgroundExecutor.execute { loadedControls.add(c) }
+ backgroundExecutor.execute {
+ if (isTerminated) return@execute
+
+ loadedControls.add(c)
+
+ // Once we have reached our requestLimit, send a request to cancel, and immediately
+ // load the results. Calls to onError() and onComplete() are not required after
+ // cancel.
+ if (loadedControls.size >= requestLimit) {
+ maybeTerminateAndRun(
+ OnCancelAndLoadRunnable(token, loadedControls, subscription, callback)
+ )
+ }
+ }
}
+
override fun onError(token: IBinder, s: String) {
- hasError = true
- _loadCancelInternal = {}
- currentProvider?.cancelLoadTimeout()
- backgroundExecutor.execute(OnLoadErrorRunnable(token, s, callback))
+ maybeTerminateAndRun(OnLoadErrorRunnable(token, s, callback))
}
override fun onComplete(token: IBinder) {
+ maybeTerminateAndRun(OnLoadRunnable(token, loadedControls, callback))
+ }
+
+ private fun maybeTerminateAndRun(postTerminateFn: Runnable) {
+ if (isTerminated) return
+
+ isTerminated = true
_loadCancelInternal = {}
- if (!hasError) {
- currentProvider?.cancelLoadTimeout()
- backgroundExecutor.execute(OnLoadRunnable(token, loadedControls, callback))
- }
+ currentProvider?.cancelLoadTimeout()
+
+ backgroundExecutor.execute(postTerminateFn)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index ae75dd4..568fb28 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -180,6 +180,11 @@
fun countFavoritesForComponent(componentName: ComponentName): Int
/**
+ * TEMPORARY for testing
+ */
+ fun resetFavorites()
+
+ /**
* Interface for structure to pass data to [ControlsFavoritingActivity].
*/
interface LoadData {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 3483339..8805694 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -365,6 +365,8 @@
componentName: ComponentName,
callback: Consumer<Boolean>
) {
+ if (seedingInProgress) return
+
Log.i(TAG, "Beginning request to seed favorites for: $componentName")
if (!confirmAvailability()) {
if (userChanging) {
@@ -495,6 +497,13 @@
}
}
+ override fun resetFavorites() {
+ executor.execute {
+ Favorites.clear()
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
+ }
+ }
+
override fun refreshStatus(componentName: ComponentName, control: Control) {
if (!confirmAvailability()) {
Log.d(TAG, "Controls not available")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 895f1d2..a6af6a1 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -63,8 +63,6 @@
) : IBinder.DeathRecipient {
val token: IBinder = Binder()
- @GuardedBy("subscriptions")
- private val subscriptions = mutableListOf<IControlsSubscription>()
private var requiresBound = false
@GuardedBy("queuedServiceMethods")
private val queuedServiceMethods: MutableSet<ServiceMethod> = ArraySet()
@@ -194,7 +192,7 @@
* Request a call to [IControlsProvider.loadSuggested].
*
* If the service is not bound, the call will be queued and the service will be bound first.
- * The service will be unbound after the controls are returned or the call times out.
+ * The service will be unbound if the call times out.
*
* @param subscriber the subscriber that manages coordination for loading controls
*/
@@ -245,9 +243,7 @@
if (DEBUG) {
Log.d(TAG, "startSubscription: $subscription")
}
- synchronized(subscriptions) {
- subscriptions.add(subscription)
- }
+
wrapper?.request(subscription, requestLimit)
}
@@ -261,9 +257,7 @@
if (DEBUG) {
Log.d(TAG, "cancelSubscription: $subscription")
}
- synchronized(subscriptions) {
- subscriptions.remove(subscription)
- }
+
wrapper?.cancel(subscription)
}
@@ -281,17 +275,6 @@
onLoadCanceller?.run()
onLoadCanceller = null
- // be sure to cancel all subscriptions
- val subs = synchronized(subscriptions) {
- ArrayList(subscriptions).also {
- subscriptions.clear()
- }
- }
-
- subs.forEach {
- wrapper?.cancel(it)
- }
-
bindService(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 208d911..a5f4298 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -16,18 +16,26 @@
package com.android.systemui.controls.ui
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ObjectAnimator
+import android.app.AlertDialog
import android.app.Dialog
import android.content.ComponentName
import android.content.Context
+import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
+import android.os.Process
import android.service.controls.Control
import android.service.controls.actions.ControlAction
import android.util.TypedValue
import android.util.Log
+import android.view.animation.AccelerateInterpolator
+import android.view.animation.DecelerateInterpolator
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.View
@@ -77,6 +85,8 @@
private const val PREF_COMPONENT = "controls_component"
private const val PREF_STRUCTURE = "controls_structure"
+ private const val FADE_IN_MILLIS = 225L
+
private val EMPTY_COMPONENT = ComponentName("", "")
private val EMPTY_STRUCTURE = StructureInfo(
EMPTY_COMPONENT,
@@ -153,7 +163,20 @@
private fun reload(parent: ViewGroup) {
if (hidden) return
- show(parent)
+
+ val fadeAnim = ObjectAnimator.ofFloat(parent, "alpha", 1.0f, 0.0f)
+ fadeAnim.setInterpolator(AccelerateInterpolator(1.0f))
+ fadeAnim.setDuration(FADE_IN_MILLIS)
+ fadeAnim.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ show(parent)
+ val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
+ showAnim.setInterpolator(DecelerateInterpolator(1.0f))
+ showAnim.setDuration(FADE_IN_MILLIS)
+ showAnim.start()
+ }
+ })
+ fadeAnim.start()
}
private fun showSeedingView(items: List<SelectionItem>) {
@@ -229,7 +252,8 @@
private fun createMenu() {
val items = arrayOf(
- context.resources.getString(R.string.controls_menu_add)
+ context.resources.getString(R.string.controls_menu_add),
+ "Reset"
)
var adapter = ArrayAdapter<String>(context, R.layout.controls_more_item, items)
@@ -249,6 +273,8 @@
when (pos) {
// 0: Add Control
0 -> startFavoritingActivity(view.context, selectedStructure)
+ // 1: TEMPORARY for reset controls
+ 1 -> showResetConfirmation()
else -> Log.w(ControlsUiController.TAG,
"Unsupported index ($pos) on 'more' menu selection")
}
@@ -275,6 +301,39 @@
})
}
+ private fun showResetConfirmation() {
+ val builder = AlertDialog.Builder(
+ context,
+ android.R.style.Theme_DeviceDefault_Dialog_Alert
+ ).apply {
+ setMessage("For testing purposes: Would you like to " +
+ "reset your favorited device controls?")
+ setPositiveButton(
+ android.R.string.ok,
+ DialogInterface.OnClickListener { dialog, _ ->
+ val userHandle = Process.myUserHandle()
+ val userContext = context.createContextAsUser(userHandle, 0)
+ val prefs = userContext.getSharedPreferences(
+ "controls_prefs", Context.MODE_PRIVATE)
+ prefs.edit().putBoolean("ControlsSeedingCompleted", false).apply()
+ controlsController.get().resetFavorites()
+ dialog.dismiss()
+ context.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
+ })
+ setNegativeButton(
+ android.R.string.cancel,
+ DialogInterface.OnClickListener {
+ dialog, _ -> dialog.cancel()
+ }
+ )
+ }
+ builder.create().apply {
+ getWindow().apply {
+ setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+ }
+ }.show()
+ }
+
private fun createDropDown(items: List<SelectionItem>) {
items.forEach {
RenderInfo.registerComponentIcon(it.componentName, it.icon)
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 4dd5e87..79def1d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -205,7 +205,9 @@
private final IWindowManager mIWindowManager;
private final Executor mBackgroundExecutor;
private final ControlsListingController mControlsListingController;
- private boolean mAnyControlsProviders = false;
+ private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>();
+ private ControlsController mControlsController;
+ private SharedPreferences mControlsPreferences;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -271,6 +273,7 @@
mBackgroundExecutor = backgroundExecutor;
mControlsListingController = controlsListingController;
mBlurUtils = blurUtils;
+ mControlsController = controlsController;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -309,45 +312,54 @@
}
});
- String preferredControlsPackage = mContext.getResources()
- .getString(com.android.systemui.R.string.config_controlsPreferredPackage);
mControlsListingController.addCallback(list -> {
- mAnyControlsProviders = !list.isEmpty();
-
- /*
- * See if any service providers match the preferred component. If they do,
- * and there are no current favorites, and we haven't successfully loaded favorites to
- * date, query the preferred component for a limited number of suggested controls.
- */
- ComponentName preferredComponent = null;
- for (ControlsServiceInfo info : list) {
- if (info.componentName.getPackageName().equals(preferredControlsPackage)) {
- preferredComponent = info.componentName;
- break;
- }
- }
-
- if (preferredComponent == null) return;
-
- SharedPreferences prefs = context.getSharedPreferences(PREFS_CONTROLS_FILE,
- Context.MODE_PRIVATE);
- boolean isSeeded = prefs.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false);
- boolean hasFavorites = controlsController.getFavorites().size() > 0;
- if (!isSeeded && !hasFavorites) {
- controlsController.seedFavoritesForComponent(
- preferredComponent,
- (accepted) -> {
- Log.i(TAG, "Controls seeded: " + accepted);
- prefs.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED,
- accepted).apply();
- }
- );
- }
+ mControlsServiceInfos = list;
});
+
+ // Need to be user-specific with the context to make sure we read the correct prefs
+ Context userContext = context.createContextAsUser(
+ new UserHandle(mUserManager.getUserHandle()), 0);
+ mControlsPreferences = userContext.getSharedPreferences(PREFS_CONTROLS_FILE,
+ Context.MODE_PRIVATE);
+
}
+ private void seedFavorites() {
+ if (mControlsServiceInfos.isEmpty()
+ || mControlsController.getFavorites().size() > 0
+ || mControlsPreferences.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false)) {
+ return;
+ }
+ /*
+ * See if any service providers match the preferred component. If they do,
+ * and there are no current favorites, and we haven't successfully loaded favorites to
+ * date, query the preferred component for a limited number of suggested controls.
+ */
+ String preferredControlsPackage = mContext.getResources()
+ .getString(com.android.systemui.R.string.config_controlsPreferredPackage);
+ ComponentName preferredComponent = null;
+ for (ControlsServiceInfo info : mControlsServiceInfos) {
+ if (info.componentName.getPackageName().equals(preferredControlsPackage)) {
+ preferredComponent = info.componentName;
+ break;
+ }
+ }
+
+ if (preferredComponent == null) {
+ Log.i(TAG, "Controls seeding: No preferred component has been set, will not seed");
+ mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, true).apply();
+ }
+
+ mControlsController.seedFavoritesForComponent(
+ preferredComponent,
+ (accepted) -> {
+ Log.i(TAG, "Controls seeded: " + accepted);
+ mControlsPreferences.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED,
+ accepted).apply();
+ });
+ }
/**
* Show the global actions dialog (creating if necessary)
@@ -393,6 +405,7 @@
awakenIfNecessary();
mDialog = createDialog();
prepareDialog();
+ seedFavorites();
// If we only have 1 item and it's a simple press action, just do this action.
if (mAdapter.getCount() == 1
@@ -2017,6 +2030,6 @@
private boolean shouldShowControls() {
return mKeyguardStateController.isUnlocked()
&& mControlsUiController.getAvailable()
- && mAnyControlsProviders;
+ && !mControlsServiceInfos.isEmpty();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index 88316f2..bb003ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -125,7 +125,7 @@
loadSubscriberCaptor.value.onSubscribe(Binder(), subscription)
canceller.run()
- verify(subscription).cancel()
+ verify(providers[0]).cancelSubscription(subscription)
}
@Test
@@ -145,7 +145,7 @@
loadSubscriberCaptor.value.onComplete(b)
canceller.run()
- verify(subscription, never()).cancel()
+ verify(providers[0], never()).cancelSubscription(subscription)
}
@Test
@@ -203,7 +203,7 @@
loadSubscriberCaptor.value.onError(b, "")
canceller.run()
- verify(subscription, never()).cancel()
+ verify(providers[0], never()).cancelSubscription(subscription)
}
@Test