Add scheme specific part to IntentFilter.

Change-Id: I063d086cdc742800b8e31ddf2942f2e9230e2785
This commit is contained in:
Dianne Hackborn
2013-06-12 16:21:38 -07:00
parent 9ddd70b92c
commit df1c0bf774
7 changed files with 249 additions and 30 deletions

View File

@@ -954,6 +954,9 @@ package android {
field public static final int spinnersShown = 16843595; // 0x101034b
field public static final int splitMotionEvents = 16843503; // 0x10102ef
field public static final int src = 16843033; // 0x1010119
field public static final int ssp = 16843744; // 0x10103e0
field public static final int sspPattern = 16843746; // 0x10103e2
field public static final int sspPrefix = 16843745; // 0x10103e1
field public static final int stackFromBottom = 16843005; // 0x10100fd
field public static final int starStyle = 16842882; // 0x1010082
field public static final int startColor = 16843165; // 0x101019d
@@ -6322,6 +6325,7 @@ package android.content {
method public final void addDataAuthority(java.lang.String, java.lang.String);
method public final void addDataPath(java.lang.String, int);
method public final void addDataScheme(java.lang.String);
method public final void addDataSchemeSpecificPart(java.lang.String, int);
method public final void addDataType(java.lang.String) throws android.content.IntentFilter.MalformedMimeTypeException;
method public final java.util.Iterator<android.content.IntentFilter.AuthorityEntry> authoritiesIterator();
method public final java.util.Iterator<java.lang.String> categoriesIterator();
@@ -6329,6 +6333,7 @@ package android.content {
method public final int countCategories();
method public final int countDataAuthorities();
method public final int countDataPaths();
method public final int countDataSchemeSpecificParts();
method public final int countDataSchemes();
method public final int countDataTypes();
method public static android.content.IntentFilter create(java.lang.String, java.lang.String);
@@ -6339,6 +6344,7 @@ package android.content {
method public final android.content.IntentFilter.AuthorityEntry getDataAuthority(int);
method public final android.os.PatternMatcher getDataPath(int);
method public final java.lang.String getDataScheme(int);
method public final android.os.PatternMatcher getDataSchemeSpecificPart(int);
method public final java.lang.String getDataType(int);
method public final int getPriority();
method public final boolean hasAction(java.lang.String);
@@ -6346,6 +6352,7 @@ package android.content {
method public final boolean hasDataAuthority(android.net.Uri);
method public final boolean hasDataPath(java.lang.String);
method public final boolean hasDataScheme(java.lang.String);
method public final boolean hasDataSchemeSpecificPart(java.lang.String);
method public final boolean hasDataType(java.lang.String);
method public final int match(android.content.ContentResolver, android.content.Intent, boolean, java.lang.String);
method public final int match(java.lang.String, java.lang.String, java.lang.String, android.net.Uri, java.util.Set<java.lang.String>, java.lang.String);
@@ -6355,6 +6362,7 @@ package android.content {
method public final int matchDataAuthority(android.net.Uri);
method public final java.util.Iterator<android.os.PatternMatcher> pathsIterator();
method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public final java.util.Iterator<android.os.PatternMatcher> schemeSpecificPartsIterator();
method public final java.util.Iterator<java.lang.String> schemesIterator();
method public final void setPriority(int);
method public final java.util.Iterator<java.lang.String> typesIterator();
@@ -6369,6 +6377,7 @@ package android.content {
field public static final int MATCH_CATEGORY_PATH = 5242880; // 0x500000
field public static final int MATCH_CATEGORY_PORT = 4194304; // 0x400000
field public static final int MATCH_CATEGORY_SCHEME = 2097152; // 0x200000
field public static final int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 5767168; // 0x580000
field public static final int MATCH_CATEGORY_TYPE = 6291456; // 0x600000
field public static final int NO_MATCH_ACTION = -3; // 0xfffffffd
field public static final int NO_MATCH_CATEGORY = -4; // 0xfffffffc

View File

@@ -50,8 +50,8 @@ import java.util.Set;
* <em>action</em>, <em>data</em>, and <em>categories</em>. For each of these
* characteristics you can provide
* multiple possible matching values (via {@link #addAction},
* {@link #addDataType}, {@link #addDataScheme} {@link #addDataAuthority},
* {@link #addDataPath}, and {@link #addCategory}, respectively).
* {@link #addDataType}, {@link #addDataScheme}, {@link #addDataSchemeSpecificPart},
* {@link #addDataAuthority}, {@link #addDataPath}, and {@link #addCategory}, respectively).
* For actions, the field
* will not be tested if no values have been given (treating it as a wildcard);
* if no data characteristics are specified, however, then the filter will
@@ -106,8 +106,15 @@ import java.util.Set;
* formal RFC schemes!</em> You should thus always use lower case letters
* for your schemes.
*
* <p><strong>Data Scheme Specific Part</strong> matches if any of the given values match
* the Intent's data scheme specific part <em>and</em> one of the data schemes in the filter
* has matched the Intent, <em>or</em> no scheme specific parts were supplied in the filter.
* The Intent scheme specific part is determined by calling
* {@link Intent#getData} and {@link android.net.Uri#getSchemeSpecificPart} on that URI.
* <em>Note that scheme specific part matching is <b>case sensitive</b>.</em>
*
* <p><strong>Data Authority</strong> matches if any of the given values match
* the Intent's data authority <em>and</em> one of the data scheme's in the filter
* the Intent's data authority <em>and</em> one of the data schemes in the filter
* has matched the Intent, <em>or</em> no authories were supplied in the filter.
* The Intent authority is determined by calling
* {@link Intent#getData} and {@link android.net.Uri#getAuthority} on that URI.
@@ -135,6 +142,7 @@ public class IntentFilter implements Parcelable {
private static final String PORT_STR = "port";
private static final String HOST_STR = "host";
private static final String AUTH_STR = "auth";
private static final String SSP_STR = "ssp";
private static final String SCHEME_STR = "scheme";
private static final String TYPE_STR = "type";
private static final String CAT_STR = "cat";
@@ -164,8 +172,8 @@ public class IntentFilter implements Parcelable {
/**
* The part of a match constant that describes the category of match
* that occurred. May be either {@link #MATCH_CATEGORY_EMPTY},
* {@link #MATCH_CATEGORY_SCHEME}, {@link #MATCH_CATEGORY_HOST},
* {@link #MATCH_CATEGORY_PORT},
* {@link #MATCH_CATEGORY_SCHEME}, {@link #MATCH_CATEGORY_SCHEME_SPECIFIC_PART},
* {@link #MATCH_CATEGORY_HOST}, {@link #MATCH_CATEGORY_PORT},
* {@link #MATCH_CATEGORY_PATH}, or {@link #MATCH_CATEGORY_TYPE}. Higher
* values indicate a better match.
*/
@@ -209,6 +217,11 @@ public class IntentFilter implements Parcelable {
* authority, and path.
*/
public static final int MATCH_CATEGORY_PATH = 0x0500000;
/**
* The filter matched an intent with the same data URI scheme and
* scheme specific part.
*/
public static final int MATCH_CATEGORY_SCHEME_SPECIFIC_PART = 0x0580000;
/**
* The filter matched an intent with the same data MIME type.
*/
@@ -236,6 +249,7 @@ public class IntentFilter implements Parcelable {
private final ArrayList<String> mActions;
private ArrayList<String> mCategories = null;
private ArrayList<String> mDataSchemes = null;
private ArrayList<PatternMatcher> mDataSchemeSpecificParts = null;
private ArrayList<AuthorityEntry> mDataAuthorities = null;
private ArrayList<PatternMatcher> mDataPaths = null;
private ArrayList<String> mDataTypes = null;
@@ -395,6 +409,9 @@ public class IntentFilter implements Parcelable {
if (o.mDataSchemes != null) {
mDataSchemes = new ArrayList<String>(o.mDataSchemes);
}
if (o.mDataSchemeSpecificParts != null) {
mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(o.mDataSchemeSpecificParts);
}
if (o.mDataAuthorities != null) {
mDataAuthorities = new ArrayList<AuthorityEntry>(o.mDataAuthorities);
}
@@ -698,6 +715,77 @@ public class IntentFilter implements Parcelable {
}
};
/**
* Add a new Intent data "scheme specific part" to match against. The filter must
* include one or more schemes (via {@link #addDataScheme}) for the
* scheme specific part to be considered. If any scheme specific parts are
* included in the filter, then an Intent's data must match one of
* them. If no scheme specific parts are included, then only the scheme must match.
*
* @param ssp Either a raw string that must exactly match the scheme specific part
* path, or a simple pattern, depending on <var>type</var>.
* @param type Determines how <var>ssp</var> will be compared to
* determine a match: either {@link PatternMatcher#PATTERN_LITERAL},
* {@link PatternMatcher#PATTERN_PREFIX}, or
* {@link PatternMatcher#PATTERN_SIMPLE_GLOB}.
*
* @see #matchData
* @see #addDataScheme
*/
public final void addDataSchemeSpecificPart(String ssp, int type) {
if (mDataSchemeSpecificParts == null) mDataSchemeSpecificParts =
new ArrayList<PatternMatcher>();
mDataSchemeSpecificParts.add(new PatternMatcher(ssp, type));
}
/**
* Return the number of data scheme specific parts in the filter.
*/
public final int countDataSchemeSpecificParts() {
return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.size() : 0;
}
/**
* Return a data scheme specific part in the filter.
*/
public final PatternMatcher getDataSchemeSpecificPart(int index) {
return mDataSchemeSpecificParts.get(index);
}
/**
* Is the given data scheme specific part included in the filter? Note that if the
* filter does not include any scheme specific parts, false will <em>always</em> be
* returned.
*
* @param data The scheme specific part that is being looked for.
*
* @return Returns true if the data string matches a scheme specific part listed in the
* filter.
*/
public final boolean hasDataSchemeSpecificPart(String data) {
if (mDataSchemeSpecificParts == null) {
return false;
}
final int numDataSchemeSpecificParts = mDataSchemeSpecificParts.size();
if (numDataSchemeSpecificParts <= 0) {
return false;
}
for (int i = 0; i < numDataSchemeSpecificParts; i++) {
final PatternMatcher pe = mDataSchemeSpecificParts.get(i);
if (pe.match(data)) {
return true;
}
}
return false;
}
/**
* Return an iterator over the filter's data scheme specific parts.
*/
public final Iterator<PatternMatcher> schemeSpecificPartsIterator() {
return mDataSchemeSpecificParts != null ? mDataSchemeSpecificParts.iterator() : null;
}
/**
* Add a new Intent data authority to match against. The filter must
* include one or more schemes (via {@link #addDataScheme}) for the
@@ -900,8 +988,6 @@ public class IntentFilter implements Parcelable {
public final int matchData(String type, String scheme, Uri data) {
final ArrayList<String> types = mDataTypes;
final ArrayList<String> schemes = mDataSchemes;
final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
final ArrayList<PatternMatcher> paths = mDataPaths;
int match = MATCH_CATEGORY_EMPTY;
@@ -917,20 +1003,34 @@ public class IntentFilter implements Parcelable {
return NO_MATCH_DATA;
}
if (authorities != null) {
int authMatch = matchDataAuthority(data);
if (authMatch >= 0) {
if (paths == null) {
match = authMatch;
} else if (hasDataPath(data.getPath())) {
match = MATCH_CATEGORY_PATH;
final ArrayList<PatternMatcher> schemeSpecificParts = mDataSchemeSpecificParts;
if (schemeSpecificParts != null) {
match = hasDataSchemeSpecificPart(data.getSchemeSpecificPart())
? MATCH_CATEGORY_SCHEME_SPECIFIC_PART : NO_MATCH_DATA;
}
if (match != MATCH_CATEGORY_SCHEME_SPECIFIC_PART) {
// If there isn't any matching ssp, we need to match an authority.
final ArrayList<AuthorityEntry> authorities = mDataAuthorities;
if (authorities != null) {
int authMatch = matchDataAuthority(data);
if (authMatch >= 0) {
final ArrayList<PatternMatcher> paths = mDataPaths;
if (paths == null) {
match = authMatch;
} else if (hasDataPath(data.getPath())) {
match = MATCH_CATEGORY_PATH;
} else {
return NO_MATCH_DATA;
}
} else {
return NO_MATCH_DATA;
}
} else {
return NO_MATCH_DATA;
}
}
// If neither an ssp nor an authority matched, we're done.
if (match == NO_MATCH_DATA) {
return NO_MATCH_DATA;
}
} else {
// Special case: match either an Intent with no data URI,
// or with a scheme: URI. This is to give a convenience for
@@ -1173,6 +1273,23 @@ public class IntentFilter implements Parcelable {
serializer.attribute(null, NAME_STR, mDataSchemes.get(i));
serializer.endTag(null, SCHEME_STR);
}
N = countDataSchemeSpecificParts();
for (int i=0; i<N; i++) {
serializer.startTag(null, SSP_STR);
PatternMatcher pe = mDataSchemeSpecificParts.get(i);
switch (pe.getType()) {
case PatternMatcher.PATTERN_LITERAL:
serializer.attribute(null, LITERAL_STR, pe.getPath());
break;
case PatternMatcher.PATTERN_PREFIX:
serializer.attribute(null, PREFIX_STR, pe.getPath());
break;
case PatternMatcher.PATTERN_SIMPLE_GLOB:
serializer.attribute(null, SGLOB_STR, pe.getPath());
break;
}
serializer.endTag(null, SSP_STR);
}
N = countDataAuthorities();
for (int i=0; i<N; i++) {
serializer.startTag(null, AUTH_STR);
@@ -1238,6 +1355,15 @@ public class IntentFilter implements Parcelable {
if (name != null) {
addDataScheme(name);
}
} else if (tagName.equals(SSP_STR)) {
String ssp = parser.getAttributeValue(null, LITERAL_STR);
if (ssp != null) {
addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_LITERAL);
} else if ((ssp=parser.getAttributeValue(null, PREFIX_STR)) != null) {
addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_PREFIX);
} else if ((ssp=parser.getAttributeValue(null, SGLOB_STR)) != null) {
addDataSchemeSpecificPart(ssp, PatternMatcher.PATTERN_SIMPLE_GLOB);
}
} else if (tagName.equals(AUTH_STR)) {
String host = parser.getAttributeValue(null, HOST_STR);
String port = parser.getAttributeValue(null, PORT_STR);
@@ -1289,6 +1415,16 @@ public class IntentFilter implements Parcelable {
du.println(sb.toString());
}
}
if (mDataSchemeSpecificParts != null) {
Iterator<PatternMatcher> it = mDataSchemeSpecificParts.iterator();
while (it.hasNext()) {
PatternMatcher pe = it.next();
sb.setLength(0);
sb.append(prefix); sb.append("Ssp: \"");
sb.append(pe); sb.append("\"");
du.println(sb.toString());
}
}
if (mDataAuthorities != null) {
Iterator<AuthorityEntry> it = mDataAuthorities.iterator();
while (it.hasNext()) {
@@ -1363,6 +1499,15 @@ public class IntentFilter implements Parcelable {
} else {
dest.writeInt(0);
}
if (mDataSchemeSpecificParts != null) {
final int N = mDataSchemeSpecificParts.size();
dest.writeInt(N);
for (int i=0; i<N; i++) {
mDataSchemeSpecificParts.get(i).writeToParcel(dest, 0);
}
} else {
dest.writeInt(0);
}
if (mDataAuthorities != null) {
final int N = mDataAuthorities.size();
dest.writeInt(N);
@@ -1428,14 +1573,21 @@ public class IntentFilter implements Parcelable {
}
int N = source.readInt();
if (N > 0) {
mDataAuthorities = new ArrayList<AuthorityEntry>();
mDataSchemeSpecificParts = new ArrayList<PatternMatcher>(N);
for (int i=0; i<N; i++) {
mDataSchemeSpecificParts.add(new PatternMatcher(source));
}
}
N = source.readInt();
if (N > 0) {
mDataAuthorities = new ArrayList<AuthorityEntry>(N);
for (int i=0; i<N; i++) {
mDataAuthorities.add(new AuthorityEntry(source));
}
}
N = source.readInt();
if (N > 0) {
mDataPaths = new ArrayList<PatternMatcher>();
mDataPaths = new ArrayList<PatternMatcher>(N);
for (int i=0; i<N; i++) {
mDataPaths.add(new PatternMatcher(source));
}

View File

@@ -3288,6 +3288,24 @@ public class PackageParser {
outInfo.addDataScheme(str);
}
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_ssp, 0);
if (str != null) {
outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_LITERAL);
}
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_sspPrefix, 0);
if (str != null) {
outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_PREFIX);
}
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_sspPattern, 0);
if (str != null) {
outInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
}
String host = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestData_host, 0);
String port = sa.getNonConfigurationString(

View File

@@ -327,6 +327,17 @@ public class ResolverActivity extends AlertActivity implements AdapterView.OnIte
// Look through the resolved filter to determine which part
// of it matched the original Intent.
Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
if (pIt != null) {
String ssp = data.getSchemeSpecificPart();
while (ssp != null && pIt.hasNext()) {
PatternMatcher p = pIt.next();
if (p.match(ssp)) {
filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
break;
}
}
}
Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
if (aIt != null) {
while (aIt.hasNext()) {
@@ -339,7 +350,7 @@ public class ResolverActivity extends AlertActivity implements AdapterView.OnIte
}
}
}
Iterator<PatternMatcher> pIt = ri.filter.pathsIterator();
pIt = ri.filter.pathsIterator();
if (pIt != null) {
String path = data.getPath();
while (path != null && pIt.hasNext()) {

View File

@@ -1624,6 +1624,27 @@
case-sensitive, unlike the formal RFC. As a result,
schemes here should always use lower case letters.</em></p> -->
<attr name="scheme" format="string" />
<!-- Specify a URI scheme specific part that must exactly match, as per
{@link android.content.IntentFilter#addDataSchemeSpecificPart
IntentFilter.addDataSchemeSpecificPart()} with
{@link android.os.PatternMatcher#PATTERN_LITERAL}. -->
<attr name="ssp" format="string" />
<!-- Specify a URI scheme specific part that must be a prefix to match, as per
{@link android.content.IntentFilter#addDataSchemeSpecificPart
IntentFilter.addDataSchemeSpecificPart()} with
{@link android.os.PatternMatcher#PATTERN_PREFIX}. -->
<attr name="sspPrefix" format="string" />
<!-- Specify a URI scheme specific part that matches a simple pattern, as per
{@link android.content.IntentFilter#addDataSchemeSpecificPart
IntentFilter.addDataSchemeSpecificPart()} with
{@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
Note that because '\' is used as an escape character when
reading the string from XML (before it is parsed as a pattern),
you will need to double-escape: for example a literal "*" would
be written as "\\*" and a literal "\" would be written as
"\\\\". This is basically the same as what you would need to
write if constructing the string in Java code. -->
<attr name="sspPattern" format="string" />
<!-- Specify a URI authority host that is handled, as per
{@link android.content.IntentFilter#addDataAuthority
IntentFilter.addDataAuthority()}.
@@ -1638,17 +1659,17 @@
<attr name="port" format="string" />
<!-- Specify a URI path that must exactly match, as per
{@link android.content.IntentFilter#addDataPath
IntentFilter.addDataAuthority()} with
IntentFilter.addDataPath()} with
{@link android.os.PatternMatcher#PATTERN_LITERAL}. -->
<attr name="path" />
<!-- Specify a URI path that must be a prefix to match, as per
{@link android.content.IntentFilter#addDataPath
IntentFilter.addDataAuthority()} with
IntentFilter.addDataPath()} with
{@link android.os.PatternMatcher#PATTERN_PREFIX}. -->
<attr name="pathPrefix" />
<!-- Specify a URI path that matches a simple pattern, as per
{@link android.content.IntentFilter#addDataPath
IntentFilter.addDataAuthority()} with
IntentFilter.addDataPath()} with
{@link android.os.PatternMatcher#PATTERN_SIMPLE_GLOB}.
Note that because '\' is used as an escape character when
reading the string from XML (before it is parsed as a pattern),

View File

@@ -2063,5 +2063,8 @@
<public type="attr" name="fromScene" />
<public type="attr" name="toScene" />
<public type="attr" name="transition" />
<public type="attr" name="ssp" />
<public type="attr" name="sspPrefix" />
<public type="attr" name="sspPattern" />
</resources>

View File

@@ -1917,15 +1917,20 @@ final class Settings {
if (tmpPa.countDataSchemes() > 0) {
Uri.Builder builder = new Uri.Builder();
builder.scheme(tmpPa.getDataScheme(0));
if (tmpPa.countDataAuthorities() > 0) {
IntentFilter.AuthorityEntry auth = tmpPa.getDataAuthority(0);
if (auth.getHost() != null) {
builder.authority(auth.getHost());
if (tmpPa.countDataSchemeSpecificParts() > 0) {
PatternMatcher path = tmpPa.getDataSchemeSpecificPart(0);
builder.opaquePart(path.getPath());
} else {
if (tmpPa.countDataAuthorities() > 0) {
IntentFilter.AuthorityEntry auth = tmpPa.getDataAuthority(0);
if (auth.getHost() != null) {
builder.authority(auth.getHost());
}
}
if (tmpPa.countDataPaths() > 0) {
PatternMatcher path = tmpPa.getDataPath(0);
builder.path(path.getPath());
}
}
if (tmpPa.countDataPaths() > 0) {
PatternMatcher path = tmpPa.getDataPath(0);
builder.path(path.getPath());
}
intent.setData(builder.build());
} else if (tmpPa.countDataTypes() > 0) {