Merge changes from topics "SliceDesc", "SliceCleanup"
* changes:
Add SliceManager#getSliceDescendants
Update slice APIs
diff --git a/api/current.txt b/api/current.txt
index c179ca8..a958482 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7068,8 +7068,8 @@
public final class Slice implements android.os.Parcelable {
ctor protected Slice(android.os.Parcel);
- method public static android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
+ method public static deprecated android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public static deprecated android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public int describeContents();
method public java.util.List<java.lang.String> getHints();
method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7155,7 +7155,10 @@
}
public class SliceManager {
+ method public android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+ method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
+ method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler);
@@ -7174,7 +7177,7 @@
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
- method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
+ method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
method public android.net.Uri onMapIntentToUri(android.content.Intent);
method public void onSlicePinned(android.net.Uri);
method public void onSliceUnpinned(android.net.Uri);
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index b8fb2e3..27cd6e5 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -21,12 +21,10 @@
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
-import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
@@ -553,16 +551,11 @@
}
/**
- * Turns a slice Uri into slice content.
- *
- * @param resolver ContentResolver to be used.
- * @param uri The URI to a slice provider
- * @param supportedSpecs List of supported specs.
- * @return The Slice provided by the app or null if none is given.
- * @see Slice
+ * @deprecated TO BE REMOVED.
*/
- public static @Nullable Slice bindSlice(ContentResolver resolver, @NonNull Uri uri,
- List<SliceSpec> supportedSpecs) {
+ @Deprecated
+ public static @Nullable Slice bindSlice(ContentResolver resolver,
+ @NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider provider = resolver.acquireProvider(uri);
if (provider == null) {
@@ -590,60 +583,11 @@
}
/**
- * Turns a slice intent into slice content. Expects an explicit intent. If there is no
- * {@link ContentProvider} associated with the given intent this will throw
- * {@link IllegalArgumentException}.
- *
- * @param context The context to use.
- * @param intent The intent associated with a slice.
- * @param supportedSpecs List of supported specs.
- * @return The Slice provided by the app or null if none is given.
- * @see Slice
- * @see SliceProvider#onMapIntentToUri(Intent)
- * @see Intent
+ * @deprecated TO BE REMOVED.
*/
+ @Deprecated
public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
- List<SliceSpec> supportedSpecs) {
- Preconditions.checkNotNull(intent, "intent");
- Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
- "Slice intent must be explicit " + intent);
- ContentResolver resolver = context.getContentResolver();
-
- // Check if the intent has data for the slice uri on it and use that
- final Uri intentData = intent.getData();
- if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
- return bindSlice(resolver, intentData, supportedSpecs);
- }
- // Otherwise ask the app
- List<ResolveInfo> providers =
- context.getPackageManager().queryIntentContentProviders(intent, 0);
- if (providers == null) {
- throw new IllegalArgumentException("Unable to resolve intent " + intent);
- }
- String authority = providers.get(0).providerInfo.authority;
- Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(authority).build();
- IContentProvider provider = resolver.acquireProvider(uri);
- if (provider == null) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- try {
- Bundle extras = new Bundle();
- extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
- extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
- new ArrayList<>(supportedSpecs));
- final Bundle res = provider.call(resolver.getPackageName(),
- SliceProvider.METHOD_MAP_INTENT, null, extras);
- if (res == null) {
- return null;
- }
- return res.getParcelable(SliceProvider.EXTRA_SLICE);
- } catch (RemoteException e) {
- // Arbitrary and not worth documenting, as Activity
- // Manager will kill this process shortly anyway.
- return null;
- } finally {
- resolver.releaseProvider(provider);
- }
+ @NonNull List<SliceSpec> supportedSpecs) {
+ return context.getSystemService(SliceManager.class).bindSlice(intent, supportedSpecs);
}
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 0c5f225d..74864cb 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -17,17 +17,29 @@
package android.app.slice;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.IContentProvider;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Pair;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@@ -39,6 +51,8 @@
@SystemService(Context.SLICE_SERVICE)
public class SliceManager {
+ private static final String TAG = "SliceManager";
+
private final ISliceManager mService;
private final Context mContext;
private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
@@ -224,6 +238,126 @@
}
/**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Not all slice providers will implement this functionality, in which case,
+ * an empty collection will be returned.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see SliceProvider#onGetSliceDescendants(Uri)
+ */
+ public @NonNull Collection<Uri> getSliceDescendants(@NonNull Uri uri) {
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ final Bundle res = provider.call(resolver.getPackageName(),
+ SliceProvider.METHOD_GET_DESCENDANTS, null, extras);
+ return res.getParcelableArrayList(SliceProvider.EXTRA_SLICE_DESCENDANTS);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get slice descendants", e);
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Turns a slice Uri into slice content.
+ *
+ * @param uri The URI to a slice provider
+ * @param supportedSpecs List of supported specs.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ */
+ public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+ Preconditions.checkNotNull(uri, "uri");
+ ContentResolver resolver = mContext.getContentResolver();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
+ final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
+ null, extras);
+ Bundle.setDefusable(res, true);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
+
+ /**
+ * Turns a slice intent into slice content. Expects an explicit intent. If there is no
+ * {@link android.content.ContentProvider} associated with the given intent this will throw
+ * {@link IllegalArgumentException}.
+ *
+ * @param intent The intent associated with a slice.
+ * @param supportedSpecs List of supported specs.
+ * @return The Slice provided by the app or null if none is given.
+ * @see Slice
+ * @see SliceProvider#onMapIntentToUri(Intent)
+ * @see Intent
+ */
+ public @Nullable Slice bindSlice(@NonNull Intent intent,
+ @NonNull List<SliceSpec> supportedSpecs) {
+ Preconditions.checkNotNull(intent, "intent");
+ Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null,
+ "Slice intent must be explicit " + intent);
+ ContentResolver resolver = mContext.getContentResolver();
+
+ // Check if the intent has data for the slice uri on it and use that
+ final Uri intentData = intent.getData();
+ if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+ return bindSlice(intentData, supportedSpecs);
+ }
+ // Otherwise ask the app
+ List<ResolveInfo> providers =
+ mContext.getPackageManager().queryIntentContentProviders(intent, 0);
+ if (providers == null) {
+ throw new IllegalArgumentException("Unable to resolve intent " + intent);
+ }
+ String authority = providers.get(0).providerInfo.authority;
+ Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).build();
+ IContentProvider provider = resolver.acquireProvider(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ try {
+ Bundle extras = new Bundle();
+ extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
+ final Bundle res = provider.call(resolver.getPackageName(),
+ SliceProvider.METHOD_MAP_INTENT, null, extras);
+ if (res == null) {
+ return null;
+ }
+ return res.getParcelable(SliceProvider.EXTRA_SLICE);
+ } catch (RemoteException e) {
+ // Arbitrary and not worth documenting, as Activity
+ // Manager will kill this process shortly anyway.
+ return null;
+ } finally {
+ resolver.releaseProvider(provider);
+ }
+ }
+
+ /**
* Class that listens to changes in {@link Slice}s.
*/
public interface SliceCallback {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 8483931..aa41f14 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -36,6 +36,9 @@
import android.os.UserHandle;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -113,11 +116,19 @@
/**
* @hide
*/
+ public static final String METHOD_GET_DESCENDANTS = "get_descendants";
+ /**
+ * @hide
+ */
public static final String EXTRA_INTENT = "slice_intent";
/**
* @hide
*/
public static final String EXTRA_SLICE = "slice";
+ /**
+ * @hide
+ */
+ public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
private static final boolean DEBUG = false;
@@ -139,14 +150,6 @@
* @see {@link Slice#HINT_PARTIAL}
*/
public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
- return onBindSlice(sliceUri);
- }
-
- /**
- * @deprecated migrating to {@link #onBindSlice(Uri, List)}
- */
- @Deprecated
- public Slice onBindSlice(Uri sliceUri) {
return null;
}
@@ -183,6 +186,20 @@
}
/**
+ * Obtains a list of slices that are descendants of the specified Uri.
+ * <p>
+ * Implementing this is optional for a SliceProvider, but does provide a good
+ * discovery mechanism for finding slice Uris.
+ *
+ * @param uri The uri to look for descendants under.
+ * @return All slices within the space.
+ * @see SliceManager#getSliceDescendants(Uri)
+ */
+ public @NonNull Collection<Uri> onGetSliceDescendants(@NonNull Uri uri) {
+ return Collections.emptyList();
+ }
+
+ /**
* This method must be overridden if an {@link IntentFilter} is specified on the SliceProvider.
* In that case, this method can be called and is expected to return a non-null Uri representing
* a slice. Otherwise this will throw {@link UnsupportedOperationException}.
@@ -290,10 +307,35 @@
"Slice binding requires the permission BIND_SLICE");
}
handleUnpinSlice(uri);
+ } else if (method.equals(METHOD_GET_DESCENDANTS)) {
+ Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+ Bundle b = new Bundle();
+ b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
+ new ArrayList<>(handleGetDescendants(uri)));
+ return b;
}
return super.call(method, arg, extras);
}
+ private Collection<Uri> handleGetDescendants(Uri uri) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ return onGetSliceDescendants(uri);
+ } else {
+ CountDownLatch latch = new CountDownLatch(1);
+ Collection<Uri>[] output = new Collection[1];
+ Handler.getMain().post(() -> {
+ output[0] = onGetSliceDescendants(uri);
+ latch.countDown();
+ });
+ try {
+ latch.await();
+ return output[0];
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
private void handlePinSlice(Uri sliceUri) {
if (Looper.myLooper() == Looper.getMainLooper()) {
onSlicePinned(sliceUri);