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

Commit 21c241e0 authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Add new Intent API for associating a ClipData with an Intent.

Allows applications to propagate multiple URI grants through an
Intent.

Later on, we should probably redefine the share actions to be
based on this ClipData with the old extras-based approach only
there for compatibility.  Even if we don't do that, though, this
allows you to do a multi-select share that grants multiple URI
permissions by stuffing the URIs in a ClipData.

Also add some documentation in various places telling people how
they can grant URI permissions.

Change-Id: Id4ba8e72c11caf7e1f1f438cb7af058d1586a37c
parent c8462e32
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -4761,6 +4761,7 @@ package android.content {
  public class ClipData implements android.os.Parcelable {
    ctor public ClipData(java.lang.CharSequence, java.lang.String[], android.content.ClipData.Item);
    ctor public ClipData(android.content.ClipDescription, android.content.ClipData.Item);
    ctor public ClipData(android.content.ClipData);
    method public void addItem(android.content.ClipData.Item);
    method public int describeContents();
    method public android.content.ClipDescription getDescription();
@@ -5378,6 +5379,7 @@ package android.content {
    method public java.lang.CharSequence[] getCharSequenceArrayExtra(java.lang.String);
    method public java.util.ArrayList<java.lang.CharSequence> getCharSequenceArrayListExtra(java.lang.String);
    method public java.lang.CharSequence getCharSequenceExtra(java.lang.String);
    method public android.content.ClipData getClipData();
    method public android.content.ComponentName getComponent();
    method public android.net.Uri getData();
    method public java.lang.String getDataString();
@@ -5461,6 +5463,7 @@ package android.content {
    method public android.content.Intent setClass(android.content.Context, java.lang.Class<?>);
    method public android.content.Intent setClassName(android.content.Context, java.lang.String);
    method public android.content.Intent setClassName(java.lang.String, java.lang.String);
    method public void setClipData(android.content.ClipData);
    method public android.content.Intent setComponent(android.content.ComponentName);
    method public android.content.Intent setData(android.net.Uri);
    method public android.content.Intent setDataAndNormalize(android.net.Uri);
@@ -5646,6 +5649,7 @@ package android.content {
    field public static final java.lang.String EXTRA_UID = "android.intent.extra.UID";
    field public static final int FILL_IN_ACTION = 1; // 0x1
    field public static final int FILL_IN_CATEGORIES = 4; // 0x4
    field public static final int FILL_IN_CLIP_DATA = 128; // 0x80
    field public static final int FILL_IN_COMPONENT = 8; // 0x8
    field public static final int FILL_IN_DATA = 2; // 0x2
    field public static final int FILL_IN_PACKAGE = 16; // 0x10
+22 −2
Original line number Diff line number Diff line
@@ -571,6 +571,17 @@ import java.util.HashMap;
 * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
 * element in their own manifest to be able to start that activity.
 *
 * <p>When starting an Activity you can set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION
 * Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION
 * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} on the Intent.  This will grant the
 * Activity access to the specific URIs in the Intent.  Access will remain
 * until the Activity has finished (it will remain across the hosting
 * process being killed and other temporary destruction).  As of
 * {@link android.os.Build.VERSION_CODES#GINGERBREAD}, if the Activity
 * was already created and a new Intent is being delivered to
 * {@link #onNewIntent(Intent)}, any newly granted URI permissions will be added
 * to the existing ones it holds.
 *
 * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
 * document for more information on permissions and security in general.
 * 
@@ -3550,6 +3561,15 @@ public class Activity extends ContextThemeWrapper
     * Call this to set the result that your activity will return to its
     * caller.
     *
     * <p>As of {@link android.os.Build.VERSION_CODES#GINGERBREAD}, the Intent
     * you supply here can have {@link Intent#FLAG_GRANT_READ_URI_PERMISSION
     * Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION
     * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} set.  This will grant the
     * Activity receiving the result access to the specific URIs in the Intent.
     * Access will remain until the Activity has finished (it will remain across the hosting
     * process being killed and other temporary destruction) and will be added
     * to any existing set of URI permissions it already holds.
     *
     * @param resultCode The result code to propagate back to the originating
     *                   activity, often RESULT_CANCELED or RESULT_OK
     * @param data The data to propagate back to the originating activity.
+13 −1
Original line number Diff line number Diff line
@@ -164,6 +164,18 @@ import java.io.PrintWriter;
 * element in their own manifest to be able to start, stop, or bind to
 * the service.
 *
 * <p>As of {@link android.os.Build.VERSION_CODES#GINGERBREAD}, when using
 * {@link Context#startService(Intent) Context.startService(Intent)}, you can
 * also set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION
 * Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION
 * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} on the Intent.  This will grant the
 * Service temporary access to the specific URIs in the Intent.  Access will
 * remain until the Service has called {@link #stopSelf(int)} for that start
 * command or a later one, or until the Service has been completely stopped.
 * This works for granting access to the other apps that have not requested
 * the permission protecting the Service, or even when the Service is not
 * exported at all.
 *
 * <p>In addition, a service can protect individual IPC calls into it with
 * permissions, by calling the
 * {@link #checkCallingPermission}
+83 −1
Original line number Diff line number Diff line
@@ -153,7 +153,7 @@ public class ClipData implements Parcelable {
    
    final Bitmap mIcon;

    final ArrayList<Item> mItems = new ArrayList<Item>();
    final ArrayList<Item> mItems;

    /**
     * Description of a single item in a ClippedData.
@@ -321,6 +321,33 @@ public class ClipData implements Parcelable {
            return "";
        }
//END_INCLUDE(coerceToText)

        @Override
        public String toString() {
            StringBuilder b = new StringBuilder(128);

            b.append("ClipData.Item { ");
            toShortString(b);
            b.append(" }");

            return b.toString();
        }

        /** @hide */
        public void toShortString(StringBuilder b) {
            if (mText != null) {
                b.append("T:");
                b.append(mText);
            } else if (mUri != null) {
                b.append("U:");
                b.append(mUri);
            } else if (mIntent != null) {
                b.append("I:");
                mIntent.toShortString(b, true, true, true, true);
            } else {
                b.append("NULL");
            }
        }
    }

    /**
@@ -336,6 +363,7 @@ public class ClipData implements Parcelable {
            throw new NullPointerException("item is null");
        }
        mIcon = null;
        mItems = new ArrayList<Item>();
        mItems.add(item);
    }

@@ -351,9 +379,22 @@ public class ClipData implements Parcelable {
            throw new NullPointerException("item is null");
        }
        mIcon = null;
        mItems = new ArrayList<Item>();
        mItems.add(item);
    }

    /**
     * Create a new clip that is a copy of another clip.  This does a deep-copy
     * of all items in the clip.
     *
     * @param other The existing ClipData that is to be copied.
     */
    public ClipData(ClipData other) {
        mClipDescription = other.mClipDescription;
        mIcon = other.mIcon;
        mItems = new ArrayList<Item>(other.mItems);
    }

    /**
     * Create a new ClipData holding data of the type
     * {@link ClipDescription#MIMETYPE_TEXT_PLAIN}.
@@ -474,6 +515,46 @@ public class ClipData implements Parcelable {
        return mItems.get(index);
    }

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder(128);

        b.append("ClipData { ");
        toShortString(b);
        b.append(" }");

        return b.toString();
    }

    /** @hide */
    public void toShortString(StringBuilder b) {
        boolean first;
        if (mClipDescription != null) {
            first = !mClipDescription.toShortString(b);
        } else {
            first = true;
        }
        if (mIcon != null) {
            if (!first) {
                b.append(' ');
            }
            first = false;
            b.append("I:");
            b.append(mIcon.getWidth());
            b.append('x');
            b.append(mIcon.getHeight());
        }
        for (int i=0; i<mItems.size(); i++) {
            if (!first) {
                b.append(' ');
            }
            first = false;
            b.append('{');
            mItems.get(i).toShortString(b);
            b.append('}');
        }
    }

    @Override
    public int describeContents() {
        return 0;
@@ -515,6 +596,7 @@ public class ClipData implements Parcelable {
        } else {
            mIcon = null;
        }
        mItems = new ArrayList<Item>();
        final int N = in.readInt();
        for (int i=0; i<N; i++) {
            CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+33 −0
Original line number Diff line number Diff line
@@ -183,6 +183,39 @@ public class ClipDescription implements Parcelable {
        }
    }

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder(128);

        b.append("ClipDescription { ");
        toShortString(b);
        b.append(" }");

        return b.toString();
    }

    /** @hide */
    public boolean toShortString(StringBuilder b) {
        boolean first = true;
        for (int i=0; i<mMimeTypes.length; i++) {
            if (!first) {
                b.append(' ');
            }
            first = false;
            b.append(mMimeTypes[i]);
        }
        if (mLabel != null) {
            if (!first) {
                b.append(' ');
            }
            first = false;
            b.append('"');
            b.append(mLabel);
            b.append('"');
        }
        return !first;
    }

    @Override
    public int describeContents() {
        return 0;
Loading