Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3b0a72f0 authored by Mady Mellor's avatar Mady Mellor Committed by Jason Monk
Browse files

Implement intent to uri mapping for slices

This adds the notion of creating a slice based on an intent. To use this
developers can implement onMapIntentToUri and return a uri based on the
intent. This enables SliceView to be populated via an intent.

Test: to be added...
Change-Id: I2abd861f2a3dff80ab8b6492a5c3205bc891d19c
parent a2dfa95e
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -6974,6 +6974,7 @@ package android.app.slice {
  public final class Slice implements android.os.Parcelable {
  public final class Slice implements android.os.Parcelable {
    ctor protected Slice(android.os.Parcel);
    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.ContentResolver, android.net.Uri);
    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
    method public int describeContents();
    method public int describeContents();
    method public java.util.List<java.lang.String> getHints();
    method public java.util.List<java.lang.String> getHints();
    method public java.util.List<android.app.slice.SliceItem> getItems();
    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 java.lang.String getType(android.net.Uri);
    method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
    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 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);
    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[], 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);
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
+2 −0
Original line number Original line Diff line number Diff line
@@ -7418,6 +7418,7 @@ package android.app.slice {
  public final class Slice implements android.os.Parcelable {
  public final class Slice implements android.os.Parcelable {
    ctor protected Slice(android.os.Parcel);
    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.ContentResolver, android.net.Uri);
    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
    method public int describeContents();
    method public int describeContents();
    method public java.util.List<java.lang.String> getHints();
    method public java.util.List<java.lang.String> getHints();
    method public java.util.List<android.app.slice.SliceItem> getItems();
    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 java.lang.String getType(android.net.Uri);
    method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
    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 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);
    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[], 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);
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
+2 −0
Original line number Original line Diff line number Diff line
@@ -7048,6 +7048,7 @@ package android.app.slice {
  public final class Slice implements android.os.Parcelable {
  public final class Slice implements android.os.Parcelable {
    ctor protected Slice(android.os.Parcel);
    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.ContentResolver, android.net.Uri);
    method public static android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent);
    method public int describeContents();
    method public int describeContents();
    method public java.util.List<java.lang.String> getHints();
    method public java.util.List<java.lang.String> getHints();
    method public java.util.List<android.app.slice.SliceItem> getItems();
    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 java.lang.String getType(android.net.Uri);
    method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
    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 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);
    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[], 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);
    method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
+58 −1
Original line number Original line Diff line number Diff line
@@ -21,9 +21,12 @@ import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.app.RemoteInput;
import android.app.slice.widget.SliceView;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Icon;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.net.Uri;
import android.os.Bundle;
import android.os.Bundle;
@@ -411,4 +414,58 @@ public final class Slice implements Parcelable {
            resolver.releaseProvider(provider);
            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);
        }
    }
}
}
+66 −12
Original line number Original line Diff line number Diff line
@@ -16,10 +16,13 @@
package android.app.slice;
package android.app.slice;


import android.Manifest.permission;
import android.Manifest.permission;
import android.annotation.NonNull;
import android.app.slice.widget.SliceView;
import android.content.ContentProvider;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.ContentValues;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.Cursor;
import android.net.Uri;
import android.net.Uri;
@@ -37,29 +40,45 @@ import android.util.Log;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CountDownLatch;


/**
/**
 * A SliceProvider allows app to provide content to be displayed in system
 * A SliceProvider allows an app to provide content to be displayed in system spaces. This content
 * spaces. This content is templated and can contain actions, and the behavior
 * is templated and can contain actions, and the behavior of how it is surfaced is specific to the
 * of how it is surfaced is specific to the system surface.
 * system surface.
 *
 * <p>
 * <p>Slices are not currently live content. They are bound once and shown to the
 * Slices are not currently live content. They are bound once and shown to the user. If the content
 * user. If the content changes due to a callback from user interaction, then
 * changes due to a callback from user interaction, then
 * {@link ContentResolver#notifyChange(Uri, ContentObserver)}
 * {@link ContentResolver#notifyChange(Uri, ContentObserver)} should be used to notify the system.
 * should be used to notify the system.</p>
 * </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>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">
 * <pre class="prettyprint">
 * {@literal
 * {@literal
 * <provider
 * <provider
 *     android:name="com.android.mypkg.MySliceProvider"
 *     android:name="com.android.mypkg.MySliceProvider"
 *     android:authorities="com.android.mypkg" />}
 *     android:authorities="com.android.mypkg" />}
 * </pre>
 * </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
 * @see Slice
 */
 */
public abstract class SliceProvider extends ContentProvider {
public abstract class SliceProvider extends ContentProvider {

    /**
    /**
     * This is the Android platform's MIME type for a slice: URI
     * This is the Android platform's MIME type for a slice: URI
     * containing a slice implemented through {@link SliceProvider}.
     * containing a slice implemented through {@link SliceProvider}.
@@ -75,6 +94,14 @@ public abstract class SliceProvider extends ContentProvider {
     * @hide
     * @hide
     */
     */
    public static final String METHOD_SLICE = "bind_slice";
    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
     * @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)).
    // TODO: Provide alternate notifyChange that takes in the slice (i.e. notifyChange(Uri, Slice)).
    public abstract Slice onBindSlice(Uri sliceUri);
    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
    @Override
    public final int update(Uri uri, ContentValues values, String selection,
    public final int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
            String[] selectionArgs) {
@@ -159,6 +200,19 @@ public abstract class SliceProvider extends ContentProvider {
            Bundle b = new Bundle();
            Bundle b = new Bundle();
            b.putParcelable(EXTRA_SLICE, s);
            b.putParcelable(EXTRA_SLICE, s);
            return b;
            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);
        return super.call(method, arg, extras);
    }
    }
Loading