diff options
Diffstat (limited to 'core/java/android/content/Intent.java')
| -rw-r--r-- | core/java/android/content/Intent.java | 229 |
1 files changed, 215 insertions, 14 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9948985..4e5598b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2315,6 +2315,11 @@ public class Intent implements Parcelable, Cloneable { /** * Used with {@link #ACTION_MAIN} to launch the browser application. * The activity should be able to browse the Internet. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.</p> */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER"; @@ -2322,6 +2327,11 @@ public class Intent implements Parcelable, Cloneable { /** * Used with {@link #ACTION_MAIN} to launch the calculator application. * The activity should be able to perform standard arithmetic operations. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.</p> */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR"; @@ -2329,6 +2339,11 @@ public class Intent implements Parcelable, Cloneable { /** * Used with {@link #ACTION_MAIN} to launch the calendar application. * The activity should be able to view and manipulate calendar entries. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.</p> */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR"; @@ -2336,6 +2351,11 @@ public class Intent implements Parcelable, Cloneable { /** * Used with {@link #ACTION_MAIN} to launch the contacts application. * The activity should be able to view and manipulate address book entries. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.</p> */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS"; @@ -2343,6 +2363,11 @@ public class Intent implements Parcelable, Cloneable { /** * Used with {@link #ACTION_MAIN} to launch the email application. * The activity should be able to send and receive email. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.</p> */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL"; @@ -2351,6 +2376,11 @@ public class Intent implements Parcelable, Cloneable { * Used with {@link #ACTION_MAIN} to launch the gallery application. * The activity should be able to view and manipulate image and video files * stored on the device. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.</p> */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY"; @@ -2358,6 +2388,11 @@ public class Intent implements Parcelable, Cloneable { /** * Used with {@link #ACTION_MAIN} to launch the maps application. * The activity should be able to show the user's current location and surroundings. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.</p> */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS"; @@ -2365,13 +2400,24 @@ public class Intent implements Parcelable, Cloneable { /** * Used with {@link #ACTION_MAIN} to launch the messaging application. * The activity should be able to send and receive text messages. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.</p> */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING"; /** * Used with {@link #ACTION_MAIN} to launch the music application. - * The activity should be able to play, browse, or manipulate music files stored on the device. + * The activity should be able to play, browse, or manipulate music files + * stored on the device. + * <p>NOTE: This should not be used as the primary key of an Intent, + * since it will not result in the app launching with the correct + * action and category. Instead, use this with + * {@link #makeMainSelectorActivity(String, String) to generate a main + * Intent with this category in the selector.</p> */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC"; @@ -2963,6 +3009,7 @@ public class Intent implements Parcelable, Cloneable { private HashSet<String> mCategories; private Bundle mExtras; private Rect mSourceBounds; + private Intent mSelector; // --------------------------------------------------------------------- @@ -2991,6 +3038,9 @@ public class Intent implements Parcelable, Cloneable { if (o.mSourceBounds != null) { this.mSourceBounds = new Rect(o.mSourceBounds); } + if (o.mSelector != null) { + this.mSelector = new Intent(o.mSelector); + } } @Override @@ -3131,6 +3181,39 @@ public class Intent implements Parcelable, Cloneable { } /** + * Make an Intent for the main activity of an application, without + * specifying a specific activity to run but giving a selector to find + * the activity. This results in a final Intent that is structured + * the same as when the application is launched from + * Home. For anything else that wants to launch an application in the + * same way, it is important that they use an Intent structured the same + * way, and can use this function to ensure this is the case. + * + * <p>The returned Intent has {@link #ACTION_MAIN} as its action, and includes the + * category {@link #CATEGORY_LAUNCHER}. This does <em>not</em> have + * {@link #FLAG_ACTIVITY_NEW_TASK} set, though typically you will want + * to do that through {@link #addFlags(int)} on the returned Intent. + * + * @param selectorAction The action name of the Intent's selector. + * @param selectorCategory The name of a category to add to the Intent's + * selector. + * @return Returns a newly created Intent that can be used to launch the + * activity as a main application entry. + * + * @see #setSelector(Intent) + */ + public static Intent makeMainSelectorActivity(String selectorAction, + String selectorCategory) { + Intent intent = new Intent(ACTION_MAIN); + intent.addCategory(CATEGORY_LAUNCHER); + Intent selector = new Intent(); + selector.setAction(selectorAction); + selector.addCategory(selectorCategory); + intent.setSelector(selector); + return intent; + } + + /** * Make an Intent that can be used to re-launch an application's task * in its base state. This is like {@link #makeMainActivity(ComponentName)}, * but also sets the flags {@link #FLAG_ACTIVITY_NEW_TASK} and @@ -3205,6 +3288,7 @@ public class Intent implements Parcelable, Cloneable { // new format Intent intent = new Intent(ACTION_VIEW); + Intent baseIntent = intent; // fetch data part, if present String data = i >= 0 ? uri.substring(0, i) : null; @@ -3214,8 +3298,9 @@ public class Intent implements Parcelable, Cloneable { // loop over contents of Intent, all name=value; while (!uri.startsWith("end", i)) { int eq = uri.indexOf('=', i); - int semi = uri.indexOf(';', eq); - String value = Uri.decode(uri.substring(eq + 1, semi)); + if (eq < 0) eq = i-1; + int semi = uri.indexOf(';', i); + String value = eq < semi ? Uri.decode(uri.substring(eq + 1, semi)) : ""; // action if (uri.startsWith("action=", i)) { @@ -3257,6 +3342,11 @@ public class Intent implements Parcelable, Cloneable { intent.mSourceBounds = Rect.unflattenFromString(value); } + // selector + else if (semi == (i+3) && uri.startsWith("SEL", i)) { + intent = new Intent(); + } + // extra else { String key = Uri.decode(uri.substring(i + 2, eq)); @@ -3280,6 +3370,12 @@ public class Intent implements Parcelable, Cloneable { i = semi + 1; } + if (intent != baseIntent) { + // The Intent had a selector; fix it up. + baseIntent.setSelector(intent); + intent = baseIntent; + } + if (data != null) { if (data.startsWith("intent:")) { data = data.substring(7); @@ -3605,7 +3701,7 @@ public class Intent implements Parcelable, Cloneable { * Return the set of all categories in the intent. If there are no categories, * returns NULL. * - * @return Set The set of categories you can examine. Do not modify! + * @return The set of categories you can examine. Do not modify! * * @see #hasCategory * @see #addCategory @@ -3615,6 +3711,16 @@ public class Intent implements Parcelable, Cloneable { } /** + * Return the specific selector associated with this Intent. If there is + * none, returns null. See {@link #setSelector} for more information. + * + * @see #setSelector + */ + public Intent getSelector() { + return mSelector; + } + + /** * Sets the ClassLoader that will be used when unmarshalling * any Parcelable values from the extras of this Intent. * @@ -4433,6 +4539,49 @@ public class Intent implements Parcelable, Cloneable { } /** + * Set a selector for this Intent. This is a modification to the kinds of + * things the Intent will match. If the selector is set, it will be used + * when trying to find entities that can handle the Intent, instead of the + * main contents of the Intent. This allows you build an Intent containing + * a generic protocol while targeting it more specifically. + * + * <p>An example of where this may be used is with things like + * {@link #CATEGORY_APP_BROWSER}. This category allows you to build an + * Intent that will launch the Browser application. However, the correct + * main entry point of an application is actually {@link #ACTION_MAIN} + * {@link #CATEGORY_LAUNCHER} with {@link #setComponent(ComponentName)} + * used to specify the actual Activity to launch. If you launch the browser + * with something different, undesired behavior may happen if the user has + * previously or later launches it the normal way, since they do not match. + * Instead, you can build an Intent with the MAIN action (but no ComponentName + * yet specified) and set a selector with {@link #ACTION_MAIN} and + * {@link #CATEGORY_APP_BROWSER} to point it specifically to the browser activity. + * + * <p>Setting a selector does not impact the behavior of + * {@link #filterEquals(Intent)} and {@link #filterHashCode()}. This is part of the + * desired behavior of a selector -- it does not impact the base meaning + * of the Intent, just what kinds of things will be matched against it + * when determining who can handle it.</p> + * + * <p>You can not use both a selector and {@link #setPackage(String)} on + * the same base Intent.</p> + * + * @param selector The desired selector Intent; set to null to not use + * a special selector. + */ + public void setSelector(Intent selector) { + if (selector == this) { + throw new IllegalArgumentException( + "Intent being set as a selector of itself"); + } + if (selector != null && mPackage != null) { + throw new IllegalArgumentException( + "Can't set selector when package name is already set"); + } + mSelector = selector; + } + + /** * Add extended data to the intent. The name must include a package * prefix, for example the app com.android.contacts would use names * like "com.android.contacts.ShowAll". @@ -5259,6 +5408,10 @@ public class Intent implements Parcelable, Cloneable { * @see #resolveActivity */ public Intent setPackage(String packageName) { + if (packageName != null && mSelector != null) { + throw new IllegalArgumentException( + "Can't set package name when selector is already set"); + } mPackage = packageName; return this; } @@ -5394,12 +5547,18 @@ public class Intent implements Parcelable, Cloneable { public static final int FILL_IN_PACKAGE = 1<<4; /** - * Use with {@link #fillIn} to allow the current package value to be + * Use with {@link #fillIn} to allow the current bounds rectangle to be * overwritten, even if it is already set. */ public static final int FILL_IN_SOURCE_BOUNDS = 1<<5; /** + * Use with {@link #fillIn} to allow the current selector to be + * overwritten, even if it is already set. + */ + public static final int FILL_IN_SELECTOR = 1<<6; + + /** * Copy the contents of <var>other</var> in to this object, but only * where fields are not defined by this object. For purposes of a field * being defined, the following pieces of data in the Intent are @@ -5419,11 +5578,13 @@ public class Intent implements Parcelable, Cloneable { * * <p>In addition, you can use the {@link #FILL_IN_ACTION}, * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, - * and {@link #FILL_IN_COMPONENT} to override the restriction where the + * {@link #FILL_IN_COMPONENT}, {@link #FILL_IN_SOURCE_BOUNDS}, and + * {@link #FILL_IN_SELECTOR} to override the restriction where the * corresponding field will not be replaced if it is already set. * * <p>Note: The component field will only be copied if {@link #FILL_IN_COMPONENT} is explicitly - * specified. + * specified. The selector will only be copied if {@link #FILL_IN_SELECTOR} is + * explicitly specified. * * <p>For example, consider Intent A with {data="foo", categories="bar"} * and Intent B with {action="gotit", data-type="some/thing", @@ -5439,7 +5600,8 @@ public class Intent implements Parcelable, Cloneable { * * @return Returns a bit mask of {@link #FILL_IN_ACTION}, * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, - * and {@link #FILL_IN_COMPONENT} indicating which fields were changed. + * {@link #FILL_IN_COMPONENT}, {@link #FILL_IN_SOURCE_BOUNDS}, and + * {@link #FILL_IN_SELECTOR} indicating which fields were changed. */ public int fillIn(Intent other, int flags) { int changes = 0; @@ -5464,8 +5626,20 @@ public class Intent implements Parcelable, Cloneable { } if (other.mPackage != null && (mPackage == null || (flags&FILL_IN_PACKAGE) != 0)) { - mPackage = other.mPackage; - changes |= FILL_IN_PACKAGE; + // Only do this if mSelector is not set. + if (mSelector == null) { + mPackage = other.mPackage; + changes |= FILL_IN_PACKAGE; + } + } + // Selector is special: it can only be set if explicitly allowed, + // for the same reason as the component name. + if (other.mSelector != null && (flags&FILL_IN_SELECTOR) != 0) { + if (mPackage == null) { + mSelector = new Intent(other.mSelector); + mPackage = null; + changes |= FILL_IN_SELECTOR; + } } // Component is special: it can -only- be set if explicitly allowed, // since otherwise the sender could force the intent somewhere the @@ -5763,6 +5937,11 @@ public class Intent implements Parcelable, Cloneable { first = false; b.append("(has extras)"); } + if (mSelector != null) { + b.append(" sel={"); + mSelector.toShortString(b, secure, comp, extras); + b.append("}"); + } } /** @@ -5823,6 +6002,21 @@ public class Intent implements Parcelable, Cloneable { uri.append("#Intent;"); + toUriInner(uri, scheme, flags); + if (mSelector != null) { + uri.append("SEL;"); + // Note that for now we are not going to try to handle the + // data part; not clear how to represent this as a URI, and + // not much utility in it. + mSelector.toUriInner(uri, null, flags); + } + + uri.append("end"); + + return uri.toString(); + } + + private void toUriInner(StringBuilder uri, String scheme, int flags) { if (scheme != null) { uri.append("scheme=").append(scheme).append(';'); } @@ -5877,10 +6071,6 @@ public class Intent implements Parcelable, Cloneable { } } } - - uri.append("end"); - - return uri.toString(); } public int describeContents() { @@ -5911,6 +6101,13 @@ public class Intent implements Parcelable, Cloneable { out.writeInt(0); } + if (mSelector != null) { + out.writeInt(1); + mSelector.writeToParcel(out, flags); + } else { + out.writeInt(0); + } + out.writeBundle(mExtras); } @@ -5952,6 +6149,10 @@ public class Intent implements Parcelable, Cloneable { mCategories = null; } + if (in.readInt() != 0) { + mSelector = new Intent(in); + } + mExtras = in.readBundle(); } |
