From d6f484027daf1a25f0ca5e84de6e68702a3fb30d Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Wed, 5 Feb 2020 13:01:04 -0500 Subject: [PATCH] Add API for requesting add controls from Provider This API allows ControlsProviderService to request SystemUI to show UI to the user to add a favorite. This can be called at any time (not just when bound). Test: atest Fixes: 148936288 Change-Id: I8740b6a105ad64994e6df701f2604a4234a49154 --- api/current.txt | 2 + .../controls/ControlsProviderService.java | 44 +++++++++++++++++++ core/res/AndroidManifest.xml | 3 +- .../controls/ControlProviderServiceTest.java | 35 +++++++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/api/current.txt b/api/current.txt index 20933e4ad7620..b289a9a9bdd8d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -32,6 +32,7 @@ package android { field public static final String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field @Deprecated public static final String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; field public static final String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE"; + field public static final String BIND_CONTROLS = "android.permission.BIND_CONTROLS"; field public static final String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; field public static final String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE"; field public static final String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE"; @@ -43410,6 +43411,7 @@ package android.service.controls { method @NonNull public abstract java.util.concurrent.Flow.Publisher publisherFor(@NonNull java.util.List); method @Nullable public java.util.concurrent.Flow.Publisher publisherForAllAvailable(); method @Nullable public java.util.concurrent.Flow.Publisher publisherForSuggested(); + method public static void requestAddControl(@NonNull android.content.Context, @NonNull android.content.ComponentName, @NonNull android.service.controls.Control); field public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService"; field @NonNull public static final String TAG = "ControlsProviderService"; } diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java index cb20db90f5497..b23d0cd4bd939 100644 --- a/core/java/android/service/controls/ControlsProviderService.java +++ b/core/java/android/service/controls/ControlsProviderService.java @@ -15,11 +15,14 @@ */ package android.service.controls; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.Service; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; @@ -51,6 +54,20 @@ public abstract class ControlsProviderService extends Service { @SdkConstant(SdkConstantType.SERVICE_ACTION) public static final String SERVICE_CONTROLS = "android.service.controls.ControlsProviderService"; + + /** + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_ADD_CONTROL = + "android.service.controls.action.ADD_CONTROL"; + + /** + * @hide + */ + public static final String EXTRA_CONTROL = + "android.service.controls.extra.CONTROL"; + /** * @hide */ @@ -325,6 +342,33 @@ public abstract class ControlsProviderService extends Service { } } + /** + * Request SystemUI to prompt the user to add a control to favorites. + * + * @param context A context + * @param componentName Component name of the {@link ControlsProviderService} + * @param control A stateless control to show to the user + */ + public static void requestAddControl(@NonNull Context context, + @NonNull ComponentName componentName, + @NonNull Control control) { + Preconditions.checkNotNull(context); + Preconditions.checkNotNull(componentName); + Preconditions.checkNotNull(control); + final ComponentName sysuiComponent = ComponentName.unflattenFromString( + context.getResources().getString( + com.android.internal.R.string.config_systemUIServiceComponent)); + Intent intent = new Intent(ACTION_ADD_CONTROL); + intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName); + intent.setPackage(sysuiComponent.getPackageName()); + if (isStatelessControl(control)) { + intent.putExtra(EXTRA_CONTROL, control); + } else { + intent.putExtra(EXTRA_CONTROL, new Control.StatelessBuilder(control).build()); + } + context.sendBroadcast(intent, Manifest.permission.BIND_CONTROLS); + } + private static class SubscriptionAdapter extends IControlsSubscription.Stub { final Subscription mSubscription; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 62e57e477ca6b..930dab9d38e1d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3081,8 +3081,7 @@ diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java index 78c88d71d9532..4c2ca7eefcc16 100644 --- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java +++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java @@ -25,9 +25,13 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.Manifest; import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; import android.content.IIntentSender; import android.content.Intent; +import android.content.res.Resources; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -44,6 +48,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -59,6 +64,11 @@ import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) public class ControlProviderServiceTest { + private static final ComponentName TEST_SYSUI_COMPONENT = + ComponentName.unflattenFromString("sysui/.test.cls"); + private static final ComponentName TEST_COMPONENT = + ComponentName.unflattenFromString("test.pkg/.test.cls"); + private IBinder mToken = new Binder(); @Mock private IControlsActionCallback.Stub mActionCallback; @@ -66,6 +76,12 @@ public class ControlProviderServiceTest { private IControlsSubscriber.Stub mSubscriber; @Mock private IIntentSender mIIntentSender; + @Mock + private Resources mResources; + @Mock + private Context mContext; + @Captor + private ArgumentCaptor mIntentArgumentCaptor; private PendingIntent mPendingIntent; private FakeControlsProviderService mControlsProviderService; @@ -81,6 +97,10 @@ public class ControlProviderServiceTest { when(mSubscriber.asBinder()).thenCallRealMethod(); when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber); + when(mResources.getString(com.android.internal.R.string.config_systemUIServiceComponent)) + .thenReturn(TEST_SYSUI_COMPONENT.flattenToString()); + when(mContext.getResources()).thenReturn(mResources); + Bundle b = new Bundle(); b.putBinder(ControlsProviderService.CALLBACK_TOKEN, mToken); Intent intent = new Intent(); @@ -223,6 +243,21 @@ public class ControlProviderServiceTest { ControlAction.RESPONSE_OK); } + @Test + public void testRequestAdd() { + Control control = new Control.StatelessBuilder("TEST_ID", mPendingIntent).build(); + ControlsProviderService.requestAddControl(mContext, TEST_COMPONENT, control); + + verify(mContext).sendBroadcast(mIntentArgumentCaptor.capture(), + eq(Manifest.permission.BIND_CONTROLS)); + Intent intent = mIntentArgumentCaptor.getValue(); + assertEquals(ControlsProviderService.ACTION_ADD_CONTROL, intent.getAction()); + assertEquals(TEST_SYSUI_COMPONENT.getPackageName(), intent.getPackage()); + assertEquals(TEST_COMPONENT, intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME)); + assertTrue(equals(control, + intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL))); + } + private static boolean equals(Control c1, Control c2) { if (c1 == c2) return true; if (c1 == null || c2 == null) return false;