Merge "Implement intent to uri mapping for slices"
This commit is contained in:
committed by
Android (Google) Code Review
commit
d28796d32c
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}.
|
||||
@@ -75,6 +94,14 @@ public abstract class SliceProvider extends ContentProvider {
|
||||
* @hide
|
||||
*/
|
||||
public static final String METHOD_SLICE = "bind_slice";
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final String METHOD_MAP_INTENT = "map_slice";
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_INTENT = "slice_intent";
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user