diff options
| author | TreeHugger Robot <treehugger-gerrit@google.com> | 2017-11-15 02:52:53 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-11-15 02:52:53 +0000 |
| commit | d28796d32cfd45ae08b787dbf80cb64bbdfbb018 (patch) | |
| tree | 00154982ebf9898af6a707591c95b64c74acc17d | |
| parent | 4f18c6c7936d3bb98e801eea14efce08a1df88cb (diff) | |
| parent | 3b0a72f0228869e17f0322948bd48fec871b5e3e (diff) | |
Merge "Implement intent to uri mapping for slices"
| -rw-r--r-- | api/current.txt | 2 | ||||
| -rw-r--r-- | api/system-current.txt | 2 | ||||
| -rw-r--r-- | api/test-current.txt | 2 | ||||
| -rw-r--r-- | core/java/android/app/slice/Slice.java | 59 | ||||
| -rw-r--r-- | core/java/android/app/slice/SliceProvider.java | 78 | ||||
| -rw-r--r-- | core/java/android/app/slice/widget/SliceView.java | 22 |
6 files changed, 148 insertions, 17 deletions
diff --git a/api/current.txt b/api/current.txt index c7b2537c6733..a1b959656e32 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6974,6 +6974,7 @@ package android.app.slice { 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); + method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent); method public int describeContents(); method public java.util.List<java.lang.String> getHints(); method public java.util.List<android.app.slice.SliceItem> getItems(); @@ -7042,6 +7043,7 @@ package android.app.slice { 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 abstract android.app.slice.Slice onBindSlice(android.net.Uri); + method public android.net.Uri onMapIntentToUri(android.content.Intent); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal); diff --git a/api/system-current.txt b/api/system-current.txt index acb81eecea3e..d4656acd4e8c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -7418,6 +7418,7 @@ package android.app.slice { 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); + method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent); method public int describeContents(); method public java.util.List<java.lang.String> getHints(); method public java.util.List<android.app.slice.SliceItem> getItems(); @@ -7486,6 +7487,7 @@ package android.app.slice { 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 abstract android.app.slice.Slice onBindSlice(android.net.Uri); + method public android.net.Uri onMapIntentToUri(android.content.Intent); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal); diff --git a/api/test-current.txt b/api/test-current.txt index 6701de3f62cc..ffcdeaefe038 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -7048,6 +7048,7 @@ package android.app.slice { 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); + method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent); method public int describeContents(); method public java.util.List<java.lang.String> getHints(); method public java.util.List<android.app.slice.SliceItem> getItems(); @@ -7116,6 +7117,7 @@ package android.app.slice { 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 abstract android.app.slice.Slice onBindSlice(android.net.Uri); + method public android.net.Uri onMapIntentToUri(android.content.Intent); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal); method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal); diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index fdf8c070b34f..616a5be3e552 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -21,9 +21,12 @@ import android.annotation.Nullable; import android.annotation.StringDef; import android.app.PendingIntent; import android.app.RemoteInput; -import android.app.slice.widget.SliceView; +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; @@ -424,4 +427,58 @@ public final class Slice implements Parcelable { resolver.releaseProvider(provider); } } + + /** + * 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. + * @return The Slice provided by the app or null if none is given. + * @see Slice + * @see SliceProvider#onMapIntentToUri(Intent) + * @see Intent + */ + public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent) { + 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); + } + // 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); + 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); + } + } } diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index da718dc9a2a2..05f4ce6eb10f 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -16,10 +16,13 @@ package android.app.slice; import android.Manifest.permission; +import android.annotation.NonNull; +import android.app.slice.widget.SliceView; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; +import android.content.IntentFilter; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; @@ -37,29 +40,45 @@ import android.util.Log; import java.util.concurrent.CountDownLatch; /** - * A SliceProvider allows app to provide content to be displayed in system - * spaces. This content is templated and can contain actions, and the behavior - * of how it is surfaced is specific to the system surface. + * A SliceProvider allows an app to provide content to be displayed in system spaces. This content + * is templated and can contain actions, and the behavior of how it is surfaced is specific to the + * system surface. + * <p> + * Slices are not currently live content. They are bound once and shown to the user. If the content + * changes due to a callback from user interaction, then + * {@link ContentResolver#notifyChange(Uri, ContentObserver)} should be used to notify the system. + * </p> + * <p> + * The provider needs to be declared in the manifest to provide the authority for the app. The + * authority for most slices is expected to match the package of the application. + * </p> * - * <p>Slices are not currently live content. They are bound once and shown to the - * user. If the content changes due to a callback from user interaction, then - * {@link ContentResolver#notifyChange(Uri, ContentObserver)} - * should be used to notify the system.</p> - * - * <p>The provider needs to be declared in the manifest to provide the authority - * for the app. The authority for most slices is expected to match the package - * of the application.</p> * <pre class="prettyprint"> * {@literal * <provider * android:name="com.android.mypkg.MySliceProvider" * android:authorities="com.android.mypkg" />} * </pre> + * <p> + * Slices can be identified by a Uri or by an Intent. To link an Intent with a slice, the provider + * must have an {@link IntentFilter} matching the slice intent. When a slice is being requested via + * an intent, {@link #onMapIntentToUri(Intent)} can be called and is expected to return an + * appropriate Uri representing the slice. + * + * <pre class="prettyprint"> + * {@literal + * <provider + * android:name="com.android.mypkg.MySliceProvider" + * android:authorities="com.android.mypkg"> + * <intent-filter> + * <action android:name="android.intent.action.MY_SLICE_INTENT" /> + * </intent-filter> + * </provider>} + * </pre> * * @see Slice */ public abstract class SliceProvider extends ContentProvider { - /** * This is the Android platform's MIME type for a slice: URI * containing a slice implemented through {@link SliceProvider}. @@ -78,6 +97,14 @@ public abstract class SliceProvider extends ContentProvider { /** * @hide */ + public static final String METHOD_MAP_INTENT = "map_slice"; + /** + * @hide + */ + public static final String EXTRA_INTENT = "slice_intent"; + /** + * @hide + */ public static final String EXTRA_SLICE = "slice"; private static final boolean DEBUG = false; @@ -98,6 +125,20 @@ public abstract class SliceProvider extends ContentProvider { // TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)). public abstract Slice onBindSlice(Uri sliceUri); + /** + * 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}. + * + * @return Uri representing the slice associated with the provided intent. + * @see {@link Slice} + * @see {@link SliceView#setSlice(Intent)} + */ + public @NonNull Uri onMapIntentToUri(Intent intent) { + throw new UnsupportedOperationException( + "This provider has not implemented intent to uri mapping"); + } + @Override public final int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { @@ -159,6 +200,19 @@ public abstract class SliceProvider extends ContentProvider { Bundle b = new Bundle(); b.putParcelable(EXTRA_SLICE, s); return b; + } else if (method.equals(METHOD_MAP_INTENT)) { + getContext().enforceCallingPermission(permission.BIND_SLICE, + "Slice binding requires the permission BIND_SLICE"); + Intent intent = extras.getParcelable(EXTRA_INTENT); + Uri uri = onMapIntentToUri(intent); + Bundle b = new Bundle(); + if (uri != null) { + Slice s = handleBindSlice(uri); + b.putParcelable(EXTRA_SLICE, s); + } else { + b.putParcelable(EXTRA_SLICE, null); + } + return b; } return super.call(method, arg, extras); } diff --git a/core/java/android/app/slice/widget/SliceView.java b/core/java/android/app/slice/widget/SliceView.java index cc13ba39905a..fa1b64ceed97 100644 --- a/core/java/android/app/slice/widget/SliceView.java +++ b/core/java/android/app/slice/widget/SliceView.java @@ -183,10 +183,25 @@ public class SliceView extends ViewGroup { } /** + * Populates this view with the {@link Slice} associated with the provided {@link Intent}. To + * use this method your app must have the permission + * {@link android.Manifest.permission#BIND_SLICE}). + * <p> + * Setting a slice differs from {@link #showSlice(Slice)} because it will ensure the view is + * updated with the slice identified by the provided intent changes. The lifecycle of this + * observer is handled by SliceView in {@link #onAttachedToWindow()} and + * {@link #onDetachedFromWindow()}. To unregister this observer outside of that you can call + * {@link #clearSlice}. + * + * @return true if a slice was found for the provided intent. * @hide */ - public void showSlice(Intent intent) { - // TODO + public boolean setSlice(@Nullable Intent intent) { + Slice s = Slice.bindSlice(mContext, intent); + if (s != null) { + return setSlice(s.getUri()); + } + return s != null; } /** @@ -199,8 +214,7 @@ public class SliceView extends ViewGroup { * is handled by SliceView in {@link #onAttachedToWindow()} and {@link #onDetachedFromWindow()}. * To unregister this observer outside of that you can call {@link #clearSlice}. * - * @return true if the a slice was found for the provided uri. - * @see #clearSlice + * @return true if a slice was found for the provided uri. */ public boolean setSlice(@NonNull Uri sliceUri) { Preconditions.checkNotNull(sliceUri, |
