diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2020-04-09 16:27:07 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-04-09 16:27:07 +0000 |
commit | 7cbba76be95e3537a92b0915360c88d12913b95e (patch) | |
tree | 048ab03feb5ca9a00aaa099782419a31c523bb1c | |
parent | 9f990dedb3c0d6a5794994bc228ee1f24e7c568b (diff) | |
parent | cd757c894c7356271feaf1a4d0b6a4bc4b40e161 (diff) |
Merge "Controls UI - Add 'reset' option for seeding" into rvc-dev
7 files changed, 200 insertions, 81 deletions
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 5d03fc51004d..7e8fec716b1f 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 @@ open class ControlsBindingControllerImpl @Inject constructor( 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 @@ open class ControlsBindingControllerImpl @Inject constructor( */ 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 @@ open class ControlsBindingControllerImpl @Inject constructor( 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 @@ open class ControlsBindingControllerImpl @Inject constructor( 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 @@ open class ControlsBindingControllerImpl @Inject constructor( 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 @@ open class ControlsBindingControllerImpl @Inject constructor( val requestLimit: Long ) : CallbackRunnable(token) { override fun doRun() { + Log.d(TAG, "LoadSubscription: Starting subscription") provider?.startSubscription(subscription, requestLimit) } } @@ -254,34 +284,54 @@ open class ControlsBindingControllerImpl @Inject constructor( 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 ae75dd4d94ab..568fb289027d 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 @@ interface ControlsController : UserAwareController { 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 34833396acef..8805694616a4 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 @@ class ControlsControllerImpl @Inject constructor ( 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 @@ class ControlsControllerImpl @Inject constructor ( } } + 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 895f1d218982..a6af6a11d8b7 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 @@ class ControlsProviderLifecycleManager( ) : 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 @@ class ControlsProviderLifecycleManager( * 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 @@ class ControlsProviderLifecycleManager( if (DEBUG) { Log.d(TAG, "startSubscription: $subscription") } - synchronized(subscriptions) { - subscriptions.add(subscription) - } + wrapper?.request(subscription, requestLimit) } @@ -261,9 +257,7 @@ class ControlsProviderLifecycleManager( if (DEBUG) { Log.d(TAG, "cancelSubscription: $subscription") } - synchronized(subscriptions) { - subscriptions.remove(subscription) - } + wrapper?.cancel(subscription) } @@ -281,17 +275,6 @@ class ControlsProviderLifecycleManager( 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 208d9117e088..a5f42983e4e0 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 @@ class ControlsUiControllerImpl @Inject constructor ( 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 @@ class ControlsUiControllerImpl @Inject constructor ( 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 @@ class ControlsUiControllerImpl @Inject constructor ( 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 @@ class ControlsUiControllerImpl @Inject constructor ( 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 @@ class ControlsUiControllerImpl @Inject constructor ( }) } + 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 4dd5e87d2c93..79def1d14a1d 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mBackgroundExecutor = backgroundExecutor; mControlsListingController = controlsListingController; mBlurUtils = blurUtils; + mControlsController = controlsController; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -309,45 +312,54 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } }); - 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; - } - } + mControlsServiceInfos = list; + }); - if (preferredComponent == null) return; + // 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); - 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(); - } - ); - } - }); } + 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, 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 88316f2d4323..bb003ee59978 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 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { loadSubscriberCaptor.value.onSubscribe(Binder(), subscription) canceller.run() - verify(subscription).cancel() + verify(providers[0]).cancelSubscription(subscription) } @Test @@ -145,7 +145,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { loadSubscriberCaptor.value.onComplete(b) canceller.run() - verify(subscription, never()).cancel() + verify(providers[0], never()).cancelSubscription(subscription) } @Test @@ -203,7 +203,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { loadSubscriberCaptor.value.onError(b, "") canceller.run() - verify(subscription, never()).cancel() + verify(providers[0], never()).cancelSubscription(subscription) } @Test |