Merge "Fix issue #5714517: App shortcuts can result in bad task intents" into ics-mr1

This commit is contained in:
Dianne Hackborn
2011-12-05 19:16:54 -08:00
committed by Android (Google) Code Review
7 changed files with 323 additions and 50 deletions

View File

@ -5334,6 +5334,7 @@ package android.content {
method public java.util.ArrayList<T> getParcelableArrayListExtra(java.lang.String); method public java.util.ArrayList<T> getParcelableArrayListExtra(java.lang.String);
method public T getParcelableExtra(java.lang.String); method public T getParcelableExtra(java.lang.String);
method public java.lang.String getScheme(); method public java.lang.String getScheme();
method public android.content.Intent getSelector();
method public java.io.Serializable getSerializableExtra(java.lang.String); method public java.io.Serializable getSerializableExtra(java.lang.String);
method public short[] getShortArrayExtra(java.lang.String); method public short[] getShortArrayExtra(java.lang.String);
method public short getShortExtra(java.lang.String, short); method public short getShortExtra(java.lang.String, short);
@ -5346,6 +5347,7 @@ package android.content {
method public boolean hasExtra(java.lang.String); method public boolean hasExtra(java.lang.String);
method public boolean hasFileDescriptors(); method public boolean hasFileDescriptors();
method public static android.content.Intent makeMainActivity(android.content.ComponentName); method public static android.content.Intent makeMainActivity(android.content.ComponentName);
method public static android.content.Intent makeMainSelectorActivity(java.lang.String, java.lang.String);
method public static android.content.Intent makeRestartActivityTask(android.content.ComponentName); method public static android.content.Intent makeRestartActivityTask(android.content.ComponentName);
method public static android.content.Intent parseIntent(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; method public static android.content.Intent parseIntent(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static android.content.Intent parseUri(java.lang.String, int) throws java.net.URISyntaxException; method public static android.content.Intent parseUri(java.lang.String, int) throws java.net.URISyntaxException;
@ -5399,6 +5401,7 @@ package android.content {
method public void setExtrasClassLoader(java.lang.ClassLoader); method public void setExtrasClassLoader(java.lang.ClassLoader);
method public android.content.Intent setFlags(int); method public android.content.Intent setFlags(int);
method public android.content.Intent setPackage(java.lang.String); method public android.content.Intent setPackage(java.lang.String);
method public void setSelector(android.content.Intent);
method public void setSourceBounds(android.graphics.Rect); method public void setSourceBounds(android.graphics.Rect);
method public android.content.Intent setType(java.lang.String); method public android.content.Intent setType(java.lang.String);
method public deprecated java.lang.String toURI(); method public deprecated java.lang.String toURI();
@ -5577,6 +5580,7 @@ package android.content {
field public static final int FILL_IN_COMPONENT = 8; // 0x8 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_DATA = 2; // 0x2
field public static final int FILL_IN_PACKAGE = 16; // 0x10 field public static final int FILL_IN_PACKAGE = 16; // 0x10
field public static final int FILL_IN_SELECTOR = 64; // 0x40
field public static final int FILL_IN_SOURCE_BOUNDS = 32; // 0x20 field public static final int FILL_IN_SOURCE_BOUNDS = 32; // 0x20
field public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304; // 0x400000 field public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304; // 0x400000
field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000 field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000

View File

@ -131,6 +131,10 @@ public class Am {
runScreenCompat(); runScreenCompat();
} else if (op.equals("display-size")) { } else if (op.equals("display-size")) {
runDisplaySize(); runDisplaySize();
} else if (op.equals("to-uri")) {
runToUri(false);
} else if (op.equals("to-intent-uri")) {
runToUri(true);
} else { } else {
throw new IllegalArgumentException("Unknown command: " + op); throw new IllegalArgumentException("Unknown command: " + op);
} }
@ -138,6 +142,7 @@ public class Am {
private Intent makeIntent() throws URISyntaxException { private Intent makeIntent() throws URISyntaxException {
Intent intent = new Intent(); Intent intent = new Intent();
Intent baseIntent = intent;
boolean hasIntentInfo = false; boolean hasIntentInfo = false;
mDebugOption = false; mDebugOption = false;
@ -152,35 +157,39 @@ public class Am {
while ((opt=nextOption()) != null) { while ((opt=nextOption()) != null) {
if (opt.equals("-a")) { if (opt.equals("-a")) {
intent.setAction(nextArgRequired()); intent.setAction(nextArgRequired());
hasIntentInfo = true; if (intent == baseIntent) {
hasIntentInfo = true;
}
} else if (opt.equals("-d")) { } else if (opt.equals("-d")) {
data = Uri.parse(nextArgRequired()); data = Uri.parse(nextArgRequired());
hasIntentInfo = true; if (intent == baseIntent) {
hasIntentInfo = true;
}
} else if (opt.equals("-t")) { } else if (opt.equals("-t")) {
type = nextArgRequired(); type = nextArgRequired();
hasIntentInfo = true; if (intent == baseIntent) {
hasIntentInfo = true;
}
} else if (opt.equals("-c")) { } else if (opt.equals("-c")) {
intent.addCategory(nextArgRequired()); intent.addCategory(nextArgRequired());
hasIntentInfo = true; if (intent == baseIntent) {
hasIntentInfo = true;
}
} else if (opt.equals("-e") || opt.equals("--es")) { } else if (opt.equals("-e") || opt.equals("--es")) {
String key = nextArgRequired(); String key = nextArgRequired();
String value = nextArgRequired(); String value = nextArgRequired();
intent.putExtra(key, value); intent.putExtra(key, value);
hasIntentInfo = true;
} else if (opt.equals("--esn")) { } else if (opt.equals("--esn")) {
String key = nextArgRequired(); String key = nextArgRequired();
intent.putExtra(key, (String) null); intent.putExtra(key, (String) null);
hasIntentInfo = true;
} else if (opt.equals("--ei")) { } else if (opt.equals("--ei")) {
String key = nextArgRequired(); String key = nextArgRequired();
String value = nextArgRequired(); String value = nextArgRequired();
intent.putExtra(key, Integer.valueOf(value)); intent.putExtra(key, Integer.valueOf(value));
hasIntentInfo = true;
} else if (opt.equals("--eu")) { } else if (opt.equals("--eu")) {
String key = nextArgRequired(); String key = nextArgRequired();
String value = nextArgRequired(); String value = nextArgRequired();
intent.putExtra(key, Uri.parse(value)); intent.putExtra(key, Uri.parse(value));
hasIntentInfo = true;
} else if (opt.equals("--eia")) { } else if (opt.equals("--eia")) {
String key = nextArgRequired(); String key = nextArgRequired();
String value = nextArgRequired(); String value = nextArgRequired();
@ -190,12 +199,10 @@ public class Am {
list[i] = Integer.valueOf(strings[i]); list[i] = Integer.valueOf(strings[i]);
} }
intent.putExtra(key, list); intent.putExtra(key, list);
hasIntentInfo = true;
} else if (opt.equals("--el")) { } else if (opt.equals("--el")) {
String key = nextArgRequired(); String key = nextArgRequired();
String value = nextArgRequired(); String value = nextArgRequired();
intent.putExtra(key, Long.valueOf(value)); intent.putExtra(key, Long.valueOf(value));
hasIntentInfo = true;
} else if (opt.equals("--ela")) { } else if (opt.equals("--ela")) {
String key = nextArgRequired(); String key = nextArgRequired();
String value = nextArgRequired(); String value = nextArgRequired();
@ -205,18 +212,18 @@ public class Am {
list[i] = Long.valueOf(strings[i]); list[i] = Long.valueOf(strings[i]);
} }
intent.putExtra(key, list); intent.putExtra(key, list);
hasIntentInfo = true;
} else if (opt.equals("--ez")) { } else if (opt.equals("--ez")) {
String key = nextArgRequired(); String key = nextArgRequired();
String value = nextArgRequired(); String value = nextArgRequired();
intent.putExtra(key, Boolean.valueOf(value)); intent.putExtra(key, Boolean.valueOf(value));
hasIntentInfo = true;
} else if (opt.equals("-n")) { } else if (opt.equals("-n")) {
String str = nextArgRequired(); String str = nextArgRequired();
ComponentName cn = ComponentName.unflattenFromString(str); ComponentName cn = ComponentName.unflattenFromString(str);
if (cn == null) throw new IllegalArgumentException("Bad component name: " + str); if (cn == null) throw new IllegalArgumentException("Bad component name: " + str);
intent.setComponent(cn); intent.setComponent(cn);
hasIntentInfo = true; if (intent == baseIntent) {
hasIntentInfo = true;
}
} else if (opt.equals("-f")) { } else if (opt.equals("-f")) {
String str = nextArgRequired(); String str = nextArgRequired();
intent.setFlags(Integer.decode(str).intValue()); intent.setFlags(Integer.decode(str).intValue());
@ -264,6 +271,9 @@ public class Am {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
} else if (opt.equals("--receiver-replace-pending")) { } else if (opt.equals("--receiver-replace-pending")) {
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
} else if (opt.equals("--selector")) {
intent.setDataAndType(data, type);
intent = new Intent();
} else if (opt.equals("-D")) { } else if (opt.equals("-D")) {
mDebugOption = true; mDebugOption = true;
} else if (opt.equals("-W")) { } else if (opt.equals("-W")) {
@ -286,25 +296,42 @@ public class Am {
} }
intent.setDataAndType(data, type); intent.setDataAndType(data, type);
final boolean hasSelector = intent != baseIntent;
if (hasSelector) {
// A selector was specified; fix up.
baseIntent.setSelector(intent);
intent = baseIntent;
}
String arg = nextArg(); String arg = nextArg();
if (arg != null) { baseIntent = null;
Intent baseIntent; if (arg == null) {
if (arg.indexOf(':') >= 0) { if (hasSelector) {
// The argument is a URI. Fully parse it, and use that result // If a selector has been specified, and no arguments
// to fill in any data not specified so far. // have been supplied for the main Intent, then we can
baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME); // assume it is ACTION_MAIN CATEGORY_LAUNCHER; we don't
} else if (arg.indexOf('/') >= 0) { // need to have a component name specified yet, the
// The argument is a component name. Build an Intent to launch // selector will take care of that.
// it.
baseIntent = new Intent(Intent.ACTION_MAIN); baseIntent = new Intent(Intent.ACTION_MAIN);
baseIntent.addCategory(Intent.CATEGORY_LAUNCHER); baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
baseIntent.setComponent(ComponentName.unflattenFromString(arg));
} else {
// Assume the argument is a package name.
baseIntent = new Intent(Intent.ACTION_MAIN);
baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
baseIntent.setPackage(arg);
} }
} else if (arg.indexOf(':') >= 0) {
// The argument is a URI. Fully parse it, and use that result
// to fill in any data not specified so far.
baseIntent = Intent.parseUri(arg, Intent.URI_INTENT_SCHEME);
} else if (arg.indexOf('/') >= 0) {
// The argument is a component name. Build an Intent to launch
// it.
baseIntent = new Intent(Intent.ACTION_MAIN);
baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
baseIntent.setComponent(ComponentName.unflattenFromString(arg));
} else {
// Assume the argument is a package name.
baseIntent = new Intent(Intent.ACTION_MAIN);
baseIntent.addCategory(Intent.CATEGORY_LAUNCHER);
baseIntent.setPackage(arg);
}
if (baseIntent != null) {
Bundle extras = intent.getExtras(); Bundle extras = intent.getExtras();
intent.replaceExtras((Bundle)null); intent.replaceExtras((Bundle)null);
Bundle uriExtras = baseIntent.getExtras(); Bundle uriExtras = baseIntent.getExtras();
@ -315,7 +342,7 @@ public class Am {
baseIntent.removeCategory(c); baseIntent.removeCategory(c);
} }
} }
intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT); intent.fillIn(baseIntent, Intent.FILL_IN_COMPONENT | Intent.FILL_IN_SELECTOR);
if (extras == null) { if (extras == null) {
extras = uriExtras; extras = uriExtras;
} else if (uriExtras != null) { } else if (uriExtras != null) {
@ -1064,6 +1091,11 @@ public class Am {
} }
} }
private void runToUri(boolean intentScheme) throws Exception {
Intent intent = makeIntent();
System.out.println(intent.toUri(intentScheme ? Intent.URI_INTENT_SCHEME : 0));
}
private class IntentReceiver extends IIntentReceiver.Stub { private class IntentReceiver extends IIntentReceiver.Stub {
private boolean mFinished = false; private boolean mFinished = false;
@ -1233,6 +1265,8 @@ public class Am {
" am monitor [--gdb <port>]\n" + " am monitor [--gdb <port>]\n" +
" am screen-compat [on|off] <PACKAGE>\n" + " am screen-compat [on|off] <PACKAGE>\n" +
" am display-size [reset|MxN]\n" + " am display-size [reset|MxN]\n" +
" am to-uri [INTENT]\n" +
" am to-intent-uri [INTENT]\n" +
"\n" + "\n" +
"am start: start an Activity. Options are:\n" + "am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" + " -D: enable debugging\n" +
@ -1284,6 +1318,10 @@ public class Am {
"\n" + "\n" +
"am display-size: override display size.\n" + "am display-size: override display size.\n" +
"\n" + "\n" +
"am to-uri: print the given Intent specification as a URI.\n" +
"\n" +
"am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
"\n" +
"<INTENT> specifications include these flags and arguments:\n" + "<INTENT> specifications include these flags and arguments:\n" +
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
" [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
@ -1308,6 +1346,7 @@ public class Am {
" [--activity-single-top] [--activity-clear-task]\n" + " [--activity-single-top] [--activity-clear-task]\n" +
" [--activity-task-on-home]\n" + " [--activity-task-on-home]\n" +
" [--receiver-registered-only] [--receiver-replace-pending]\n" + " [--receiver-registered-only] [--receiver-replace-pending]\n" +
" [--selector]\n" +
" [<URI> | <PACKAGE> | <COMPONENT>]\n" " [<URI> | <PACKAGE> | <COMPONENT>]\n"
); );
} }

View File

@ -2315,6 +2315,11 @@ public class Intent implements Parcelable, Cloneable {
/** /**
* Used with {@link #ACTION_MAIN} to launch the browser application. * Used with {@link #ACTION_MAIN} to launch the browser application.
* The activity should be able to browse the Internet. * 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) @SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER"; 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. * Used with {@link #ACTION_MAIN} to launch the calculator application.
* The activity should be able to perform standard arithmetic operations. * 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) @SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR"; 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. * Used with {@link #ACTION_MAIN} to launch the calendar application.
* The activity should be able to view and manipulate calendar entries. * 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) @SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR"; 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. * Used with {@link #ACTION_MAIN} to launch the contacts application.
* The activity should be able to view and manipulate address book entries. * 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) @SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS"; 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. * Used with {@link #ACTION_MAIN} to launch the email application.
* The activity should be able to send and receive email. * 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) @SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL"; 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. * Used with {@link #ACTION_MAIN} to launch the gallery application.
* The activity should be able to view and manipulate image and video files * The activity should be able to view and manipulate image and video files
* stored on the device. * 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) @SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY"; 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. * Used with {@link #ACTION_MAIN} to launch the maps application.
* The activity should be able to show the user's current location and surroundings. * 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) @SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS"; 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. * Used with {@link #ACTION_MAIN} to launch the messaging application.
* The activity should be able to send and receive text messages. * 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) @SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING"; public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING";
/** /**
* Used with {@link #ACTION_MAIN} to launch the music application. * 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) @SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC"; 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 HashSet<String> mCategories;
private Bundle mExtras; private Bundle mExtras;
private Rect mSourceBounds; private Rect mSourceBounds;
private Intent mSelector;
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -2991,6 +3038,9 @@ public class Intent implements Parcelable, Cloneable {
if (o.mSourceBounds != null) { if (o.mSourceBounds != null) {
this.mSourceBounds = new Rect(o.mSourceBounds); this.mSourceBounds = new Rect(o.mSourceBounds);
} }
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
} }
@Override @Override
@ -3130,6 +3180,39 @@ public class Intent implements Parcelable, Cloneable {
return intent; return intent;
} }
/**
* 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 * Make an Intent that can be used to re-launch an application's task
* in its base state. This is like {@link #makeMainActivity(ComponentName)}, * in its base state. This is like {@link #makeMainActivity(ComponentName)},
@ -3205,6 +3288,7 @@ public class Intent implements Parcelable, Cloneable {
// new format // new format
Intent intent = new Intent(ACTION_VIEW); Intent intent = new Intent(ACTION_VIEW);
Intent baseIntent = intent;
// fetch data part, if present // fetch data part, if present
String data = i >= 0 ? uri.substring(0, i) : null; 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; // loop over contents of Intent, all name=value;
while (!uri.startsWith("end", i)) { while (!uri.startsWith("end", i)) {
int eq = uri.indexOf('=', i); int eq = uri.indexOf('=', i);
int semi = uri.indexOf(';', eq); if (eq < 0) eq = i-1;
String value = Uri.decode(uri.substring(eq + 1, semi)); int semi = uri.indexOf(';', i);
String value = eq < semi ? Uri.decode(uri.substring(eq + 1, semi)) : "";
// action // action
if (uri.startsWith("action=", i)) { if (uri.startsWith("action=", i)) {
@ -3257,6 +3342,11 @@ public class Intent implements Parcelable, Cloneable {
intent.mSourceBounds = Rect.unflattenFromString(value); intent.mSourceBounds = Rect.unflattenFromString(value);
} }
// selector
else if (semi == (i+3) && uri.startsWith("SEL", i)) {
intent = new Intent();
}
// extra // extra
else { else {
String key = Uri.decode(uri.substring(i + 2, eq)); String key = Uri.decode(uri.substring(i + 2, eq));
@ -3280,6 +3370,12 @@ public class Intent implements Parcelable, Cloneable {
i = semi + 1; i = semi + 1;
} }
if (intent != baseIntent) {
// The Intent had a selector; fix it up.
baseIntent.setSelector(intent);
intent = baseIntent;
}
if (data != null) { if (data != null) {
if (data.startsWith("intent:")) { if (data.startsWith("intent:")) {
data = data.substring(7); 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, * Return the set of all categories in the intent. If there are no categories,
* returns NULL. * 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 #hasCategory
* @see #addCategory * @see #addCategory
@ -3614,6 +3710,16 @@ public class Intent implements Parcelable, Cloneable {
return mCategories; return mCategories;
} }
/**
* 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 * Sets the ClassLoader that will be used when unmarshalling
* any Parcelable values from the extras of this Intent. * any Parcelable values from the extras of this Intent.
@ -4432,6 +4538,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 * Add extended data to the intent. The name must include a package
* prefix, for example the app com.android.contacts would use names * prefix, for example the app com.android.contacts would use names
@ -5259,6 +5408,10 @@ public class Intent implements Parcelable, Cloneable {
* @see #resolveActivity * @see #resolveActivity
*/ */
public Intent setPackage(String packageName) { 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; mPackage = packageName;
return this; return this;
} }
@ -5394,11 +5547,17 @@ public class Intent implements Parcelable, Cloneable {
public static final int FILL_IN_PACKAGE = 1<<4; 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. * overwritten, even if it is already set.
*/ */
public static final int FILL_IN_SOURCE_BOUNDS = 1<<5; 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 * 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 * where fields are not defined by this object. For purposes of a field
@ -5419,11 +5578,13 @@ public class Intent implements Parcelable, Cloneable {
* *
* <p>In addition, you can use the {@link #FILL_IN_ACTION}, * <p>In addition, you can use the {@link #FILL_IN_ACTION},
* {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, * {@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. * 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 * <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"} * <p>For example, consider Intent A with {data="foo", categories="bar"}
* and Intent B with {action="gotit", data-type="some/thing", * 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}, * @return Returns a bit mask of {@link #FILL_IN_ACTION},
* {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, * {@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) { public int fillIn(Intent other, int flags) {
int changes = 0; int changes = 0;
@ -5464,8 +5626,20 @@ public class Intent implements Parcelable, Cloneable {
} }
if (other.mPackage != null if (other.mPackage != null
&& (mPackage == null || (flags&FILL_IN_PACKAGE) != 0)) { && (mPackage == null || (flags&FILL_IN_PACKAGE) != 0)) {
mPackage = other.mPackage; // Only do this if mSelector is not set.
changes |= FILL_IN_PACKAGE; 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, // Component is special: it can -only- be set if explicitly allowed,
// since otherwise the sender could force the intent somewhere the // since otherwise the sender could force the intent somewhere the
@ -5763,6 +5937,11 @@ public class Intent implements Parcelable, Cloneable {
first = false; first = false;
b.append("(has extras)"); 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;"); 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) { if (scheme != null) {
uri.append("scheme=").append(scheme).append(';'); 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() { public int describeContents() {
@ -5911,6 +6101,13 @@ public class Intent implements Parcelable, Cloneable {
out.writeInt(0); out.writeInt(0);
} }
if (mSelector != null) {
out.writeInt(1);
mSelector.writeToParcel(out, flags);
} else {
out.writeInt(0);
}
out.writeBundle(mExtras); out.writeBundle(mExtras);
} }
@ -5952,6 +6149,10 @@ public class Intent implements Parcelable, Cloneable {
mCategories = null; mCategories = null;
} }
if (in.readInt() != 0) {
mSelector = new Intent(in);
}
mExtras = in.readBundle(); mExtras = in.readBundle();
} }

View File

@ -1151,8 +1151,7 @@ public class DatabaseHelper extends SQLiteOpenHelper {
intent.setComponent(cn); intent.setComponent(cn);
title = info.loadLabel(packageManager).toString(); title = info.loadLabel(packageManager).toString();
} else if (category != null) { } else if (category != null) {
intent = new Intent(Intent.ACTION_MAIN, null); intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category);
intent.addCategory(category);
title = ""; title = "";
} else { } else {
Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutStr Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutStr

View File

@ -1638,8 +1638,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (down && repeatCount == 0) { if (down && repeatCount == 0) {
String category = sApplicationLaunchKeyCategories.get(keyCode); String category = sApplicationLaunchKeyCategories.get(keyCode);
if (category != null) { if (category != null) {
Intent intent = new Intent(Intent.ACTION_MAIN); Intent intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, category);
intent.addCategory(category);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try { try {
mContext.startActivity(intent); mContext.startActivity(intent);

View File

@ -54,8 +54,17 @@ class TaskRecord extends ThumbnailHolder {
void setIntent(Intent _intent, ActivityInfo info) { void setIntent(Intent _intent, ActivityInfo info) {
stringName = null; stringName = null;
if (info.targetActivity == null) { if (info.targetActivity == null) {
if (_intent != null) {
// If this Intent has a selector, we want to clear it for the
// recent task since it is not relevant if the user later wants
// to re-launch the app.
if (_intent.getSelector() != null) {
_intent = new Intent(_intent);
_intent.setSelector(null);
}
}
intent = _intent; intent = _intent;
realActivity = _intent != null ? _intent.getComponent() : null; realActivity = _intent != null ? _intent.getComponent() : null;
origActivity = null; origActivity = null;
@ -65,6 +74,7 @@ class TaskRecord extends ThumbnailHolder {
if (_intent != null) { if (_intent != null) {
Intent targetIntent = new Intent(_intent); Intent targetIntent = new Intent(_intent);
targetIntent.setComponent(targetComponent); targetIntent.setComponent(targetComponent);
targetIntent.setSelector(null);
intent = targetIntent; intent = targetIntent;
realActivity = targetComponent; realActivity = targetComponent;
origActivity = _intent.getComponent(); origActivity = _intent.getComponent();

View File

@ -2162,6 +2162,9 @@ public class PackageManagerService extends IPackageManager.Stub {
int flags, List<ResolveInfo> query, int priority) { int flags, List<ResolveInfo> query, int priority) {
// writer // writer
synchronized (mPackages) { synchronized (mPackages) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
}
if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION); if (DEBUG_PREFERRED) intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
List<PreferredActivity> prefs = List<PreferredActivity> prefs =
mSettings.mPreferredActivities.queryIntent(intent, resolvedType, mSettings.mPreferredActivities.queryIntent(intent, resolvedType,
@ -2242,7 +2245,13 @@ public class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntentActivities(Intent intent, public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags) { String resolvedType, int flags) {
final ComponentName comp = intent.getComponent(); ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) { if (comp != null) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ActivityInfo ai = getActivityInfo(comp, flags); final ActivityInfo ai = getActivityInfo(comp, flags);
@ -2440,6 +2449,12 @@ public class PackageManagerService extends IPackageManager.Stub {
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) { public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) {
ComponentName comp = intent.getComponent(); ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) { if (comp != null) {
List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
ActivityInfo ai = getReceiverInfo(comp, flags); ActivityInfo ai = getReceiverInfo(comp, flags);
@ -2478,7 +2493,13 @@ public class PackageManagerService extends IPackageManager.Stub {
} }
public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags) { public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags) {
final ComponentName comp = intent.getComponent(); ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) { if (comp != null) {
final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
final ServiceInfo si = getServiceInfo(comp, flags); final ServiceInfo si = getServiceInfo(comp, flags);