aboutsummaryrefslogtreecommitdiffstats
path: root/ide_common/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'ide_common/src/com')
-rw-r--r--ide_common/src/com/android/ide/common/xml/AndroidManifestParser.java671
-rw-r--r--ide_common/src/com/android/ide/common/xml/ManifestData.java747
2 files changed, 1418 insertions, 0 deletions
diff --git a/ide_common/src/com/android/ide/common/xml/AndroidManifestParser.java b/ide_common/src/com/android/ide/common/xml/AndroidManifestParser.java
new file mode 100644
index 0000000..38dc1c4
--- /dev/null
+++ b/ide_common/src/com/android/ide/common/xml/AndroidManifestParser.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.xml;
+
+import com.android.SdkConstants;
+import com.android.ide.common.xml.ManifestData.Activity;
+import com.android.ide.common.xml.ManifestData.Instrumentation;
+import com.android.ide.common.xml.ManifestData.SupportsScreens;
+import com.android.ide.common.xml.ManifestData.UsesConfiguration;
+import com.android.ide.common.xml.ManifestData.UsesFeature;
+import com.android.ide.common.xml.ManifestData.UsesLibrary;
+import com.android.io.IAbstractFile;
+import com.android.io.IAbstractFolder;
+import com.android.io.StreamException;
+import com.android.resources.Keyboard;
+import com.android.resources.Navigation;
+import com.android.resources.TouchScreen;
+import com.android.xml.AndroidManifest;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+public class AndroidManifestParser {
+
+ private final static int LEVEL_TOP = 0;
+ private final static int LEVEL_INSIDE_MANIFEST = 1;
+ private final static int LEVEL_INSIDE_APPLICATION = 2;
+ private final static int LEVEL_INSIDE_APP_COMPONENT = 3;
+ private final static int LEVEL_INSIDE_INTENT_FILTER = 4;
+
+ private final static String ACTION_MAIN = "android.intent.action.MAIN"; //$NON-NLS-1$
+ private final static String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER"; //$NON-NLS-1$
+
+ public interface ManifestErrorHandler extends ErrorHandler {
+ /**
+ * Handles a parsing error and an optional line number.
+ */
+ void handleError(Exception exception, int lineNumber);
+
+ /**
+ * Checks that a class is valid and can be used in the Android Manifest.
+ * <p/>
+ * Errors are put as {@code org.eclipse.core.resources.IMarker} on the manifest file.
+ *
+ * @param locator
+ * @param className the fully qualified name of the class to test.
+ * @param superClassName the fully qualified name of the class it is supposed to extend.
+ * @param testVisibility if <code>true</code>, the method will check the visibility of
+ * the class or of its constructors.
+ */
+ void checkClass(Locator locator, String className, String superClassName,
+ boolean testVisibility);
+ }
+
+ /**
+ * XML error & data handler used when parsing the AndroidManifest.xml file.
+ * <p/>
+ * During parsing this will fill up the {@link ManifestData} object given to the constructor
+ * and call out errors to the given {@link ManifestErrorHandler}.
+ */
+ private static class ManifestHandler extends DefaultHandler {
+
+ //--- temporary data/flags used during parsing
+ private final ManifestData mManifestData;
+ private final ManifestErrorHandler mErrorHandler;
+ private int mCurrentLevel = 0;
+ private int mValidLevel = 0;
+ private Activity mCurrentActivity = null;
+ private Locator mLocator;
+
+ /**
+ * Creates a new {@link ManifestHandler}.
+ *
+ * @param manifestFile The manifest file being parsed. Can be null.
+ * @param manifestData Class containing the manifest info obtained during the parsing.
+ * @param errorHandler An optional error handler.
+ */
+ ManifestHandler(IAbstractFile manifestFile, ManifestData manifestData,
+ ManifestErrorHandler errorHandler) {
+ super();
+ mManifestData = manifestData;
+ mErrorHandler = errorHandler;
+ }
+
+ /* (non-Javadoc)
+ * @see org.xml.sax.helpers.DefaultHandler#setDocumentLocator(org.xml.sax.Locator)
+ */
+ @Override
+ public void setDocumentLocator(Locator locator) {
+ mLocator = locator;
+ super.setDocumentLocator(locator);
+ }
+
+ /* (non-Javadoc)
+ * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String,
+ * java.lang.String, org.xml.sax.Attributes)
+ */
+ @Override
+ public void startElement(String uri, String localName, String name, Attributes attributes)
+ throws SAXException {
+ try {
+ if (mManifestData == null) {
+ return;
+ }
+
+ // if we're at a valid level
+ if (mValidLevel == mCurrentLevel) {
+ String value;
+ switch (mValidLevel) {
+ case LEVEL_TOP:
+ if (AndroidManifest.NODE_MANIFEST.equals(localName)) {
+ // lets get the package name.
+ mManifestData.mPackage = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_PACKAGE,
+ false /* hasNamespace */);
+
+ // and the versionCode
+ String tmp = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_VERSIONCODE, true);
+ if (tmp != null) {
+ try {
+ mManifestData.mVersionCode = Integer.valueOf(tmp);
+ } catch (NumberFormatException e) {
+ // keep null in the field.
+ }
+ }
+ mValidLevel++;
+ }
+ break;
+ case LEVEL_INSIDE_MANIFEST:
+ if (AndroidManifest.NODE_APPLICATION.equals(localName)) {
+ value = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_PROCESS,
+ true /* hasNamespace */);
+ if (value != null) {
+ mManifestData.addProcessName(value);
+ }
+
+ value = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_DEBUGGABLE,
+ true /* hasNamespace*/);
+ if (value != null) {
+ mManifestData.mDebuggable = Boolean.parseBoolean(value);
+ }
+
+ mValidLevel++;
+ } else if (AndroidManifest.NODE_USES_SDK.equals(localName)) {
+ mManifestData.setMinSdkVersionString(getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_MIN_SDK_VERSION,
+ true /* hasNamespace */));
+ mManifestData.setTargetSdkVersionString(getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_TARGET_SDK_VERSION,
+ true /* hasNamespace */));
+ } else if (AndroidManifest.NODE_INSTRUMENTATION.equals(localName)) {
+ processInstrumentationNode(attributes);
+
+ } else if (AndroidManifest.NODE_SUPPORTS_SCREENS.equals(localName)) {
+ processSupportsScreensNode(attributes);
+
+ } else if (AndroidManifest.NODE_USES_CONFIGURATION.equals(localName)) {
+ processUsesConfiguration(attributes);
+
+ } else if (AndroidManifest.NODE_USES_FEATURE.equals(localName)) {
+ UsesFeature feature = new UsesFeature();
+
+ // get the name
+ value = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (value != null) {
+ feature.mName = value;
+ }
+
+ // read the required attribute
+ value = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_REQUIRED,
+ true /*hasNamespace*/);
+ if (value != null) {
+ Boolean b = Boolean.valueOf(value);
+ if (b != null) {
+ feature.mRequired = b;
+ }
+ }
+
+ // read the gl es attribute
+ value = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_GLESVERSION,
+ true /*hasNamespace*/);
+ if (value != null) {
+ try {
+ int version = Integer.decode(value);
+ feature.mGlEsVersion = version;
+ } catch (NumberFormatException e) {
+ // ignore
+ }
+
+ }
+
+ mManifestData.mFeatures.add(feature);
+ }
+ break;
+ case LEVEL_INSIDE_APPLICATION:
+ if (AndroidManifest.NODE_ACTIVITY.equals(localName)) {
+ processActivityNode(attributes);
+ mValidLevel++;
+ } else if (AndroidManifest.NODE_SERVICE.equals(localName)) {
+ processNode(attributes, SdkConstants.CLASS_SERVICE);
+ mValidLevel++;
+ } else if (AndroidManifest.NODE_RECEIVER.equals(localName)) {
+ processNode(attributes, SdkConstants.CLASS_BROADCASTRECEIVER);
+ mValidLevel++;
+ } else if (AndroidManifest.NODE_PROVIDER.equals(localName)) {
+ processNode(attributes, SdkConstants.CLASS_CONTENTPROVIDER);
+ mValidLevel++;
+ } else if (AndroidManifest.NODE_USES_LIBRARY.equals(localName)) {
+ value = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (value != null) {
+ UsesLibrary library = new UsesLibrary();
+ library.mName = value;
+
+ // read the required attribute
+ value = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_REQUIRED,
+ true /*hasNamespace*/);
+ if (value != null) {
+ Boolean b = Boolean.valueOf(value);
+ if (b != null) {
+ library.mRequired = b;
+ }
+ }
+
+ mManifestData.mLibraries.add(library);
+ }
+ }
+ break;
+ case LEVEL_INSIDE_APP_COMPONENT:
+ // only process this level if we are in an activity
+ if (mCurrentActivity != null &&
+ AndroidManifest.NODE_INTENT.equals(localName)) {
+ mCurrentActivity.resetIntentFilter();
+ mValidLevel++;
+ }
+ break;
+ case LEVEL_INSIDE_INTENT_FILTER:
+ if (mCurrentActivity != null) {
+ if (AndroidManifest.NODE_ACTION.equals(localName)) {
+ // get the name attribute
+ String action = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (action != null) {
+ mCurrentActivity.setHasAction(true);
+ mCurrentActivity.setHasMainAction(
+ ACTION_MAIN.equals(action));
+ }
+ } else if (AndroidManifest.NODE_CATEGORY.equals(localName)) {
+ String category = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (CATEGORY_LAUNCHER.equals(category)) {
+ mCurrentActivity.setHasLauncherCategory(true);
+ }
+ }
+
+ // no need to increase mValidLevel as we don't process anything
+ // below this level.
+ }
+ break;
+ }
+ }
+
+ mCurrentLevel++;
+ } finally {
+ super.startElement(uri, localName, name, attributes);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String,
+ * java.lang.String)
+ */
+ @Override
+ public void endElement(String uri, String localName, String name) throws SAXException {
+ try {
+ if (mManifestData == null) {
+ return;
+ }
+
+ // decrement the levels.
+ if (mValidLevel == mCurrentLevel) {
+ mValidLevel--;
+ }
+ mCurrentLevel--;
+
+ // if we're at a valid level
+ // process the end of the element
+ if (mValidLevel == mCurrentLevel) {
+ switch (mValidLevel) {
+ case LEVEL_INSIDE_APPLICATION:
+ mCurrentActivity = null;
+ break;
+ case LEVEL_INSIDE_APP_COMPONENT:
+ // if we found both a main action and a launcher category, this is our
+ // launcher activity!
+ if (mManifestData.mLauncherActivity == null &&
+ mCurrentActivity != null &&
+ mCurrentActivity.isHomeActivity() &&
+ mCurrentActivity.isExported()) {
+ mManifestData.mLauncherActivity = mCurrentActivity;
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+ } finally {
+ super.endElement(uri, localName, name);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException)
+ */
+ @Override
+ public void error(SAXParseException e) {
+ if (mErrorHandler != null) {
+ mErrorHandler.handleError(e, e.getLineNumber());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException)
+ */
+ @Override
+ public void fatalError(SAXParseException e) {
+ if (mErrorHandler != null) {
+ mErrorHandler.handleError(e, e.getLineNumber());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException)
+ */
+ @Override
+ public void warning(SAXParseException e) throws SAXException {
+ if (mErrorHandler != null) {
+ mErrorHandler.warning(e);
+ }
+ }
+
+ /**
+ * Processes the activity node.
+ * @param attributes the attributes for the activity node.
+ */
+ private void processActivityNode(Attributes attributes) {
+ // lets get the activity name, and add it to the list
+ String activityName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (activityName != null) {
+ activityName = AndroidManifest.combinePackageAndClassName(mManifestData.mPackage,
+ activityName);
+
+ // get the exported flag.
+ String exportedStr = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_EXPORTED, true);
+ boolean exported = exportedStr == null ||
+ exportedStr.toLowerCase(Locale.US).equals("true"); //$NON-NLS-1$
+ mCurrentActivity = new Activity(activityName, exported);
+ mManifestData.mActivities.add(mCurrentActivity);
+
+ if (mErrorHandler != null) {
+ mErrorHandler.checkClass(mLocator, activityName, SdkConstants.CLASS_ACTIVITY,
+ true /* testVisibility */);
+ }
+ } else {
+ // no activity found! Aapt will output an error,
+ // so we don't have to do anything
+ mCurrentActivity = null;
+ }
+
+ String processName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_PROCESS,
+ true /* hasNamespace */);
+ if (processName != null) {
+ mManifestData.addProcessName(processName);
+ }
+ }
+
+ /**
+ * Processes the service/receiver/provider nodes.
+ * @param attributes the attributes for the activity node.
+ * @param superClassName the fully qualified name of the super class that this
+ * node is representing
+ */
+ private void processNode(Attributes attributes, String superClassName) {
+ // lets get the class name, and check it if required.
+ String serviceName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (serviceName != null) {
+ serviceName = AndroidManifest.combinePackageAndClassName(mManifestData.mPackage,
+ serviceName);
+
+ if (mErrorHandler != null) {
+ mErrorHandler.checkClass(mLocator, serviceName, superClassName,
+ false /* testVisibility */);
+ }
+ }
+
+ String processName = getAttributeValue(attributes, AndroidManifest.ATTRIBUTE_PROCESS,
+ true /* hasNamespace */);
+ if (processName != null) {
+ mManifestData.addProcessName(processName);
+ }
+ }
+
+ /**
+ * Processes the instrumentation node.
+ * @param attributes the attributes for the instrumentation node.
+ */
+ private void processInstrumentationNode(Attributes attributes) {
+ // lets get the class name, and check it if required.
+ String instrumentationName = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_NAME,
+ true /* hasNamespace */);
+ if (instrumentationName != null) {
+ String instrClassName = AndroidManifest.combinePackageAndClassName(
+ mManifestData.mPackage, instrumentationName);
+ String targetPackage = getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_TARGET_PACKAGE,
+ true /* hasNamespace */);
+ mManifestData.mInstrumentations.add(
+ new Instrumentation(instrClassName, targetPackage));
+ if (mErrorHandler != null) {
+ mErrorHandler.checkClass(mLocator, instrClassName,
+ SdkConstants.CLASS_INSTRUMENTATION, true /* testVisibility */);
+ }
+ }
+ }
+
+ /**
+ * Processes the supports-screens node.
+ * @param attributes the attributes for the supports-screens node.
+ */
+ private void processSupportsScreensNode(Attributes attributes) {
+ mManifestData.mSupportsScreensFromManifest = new SupportsScreens();
+
+ mManifestData.mSupportsScreensFromManifest.setResizeable(getAttributeBooleanValue(
+ attributes, AndroidManifest.ATTRIBUTE_RESIZEABLE, true /*hasNamespace*/));
+
+ mManifestData.mSupportsScreensFromManifest.setAnyDensity(getAttributeBooleanValue(
+ attributes, AndroidManifest.ATTRIBUTE_ANYDENSITY, true /*hasNamespace*/));
+
+ mManifestData.mSupportsScreensFromManifest.setSmallScreens(getAttributeBooleanValue(
+ attributes, AndroidManifest.ATTRIBUTE_SMALLSCREENS, true /*hasNamespace*/));
+
+ mManifestData.mSupportsScreensFromManifest.setNormalScreens(getAttributeBooleanValue(
+ attributes, AndroidManifest.ATTRIBUTE_NORMALSCREENS, true /*hasNamespace*/));
+
+ mManifestData.mSupportsScreensFromManifest.setLargeScreens(getAttributeBooleanValue(
+ attributes, AndroidManifest.ATTRIBUTE_LARGESCREENS, true /*hasNamespace*/));
+ }
+
+ /**
+ * Processes the supports-screens node.
+ * @param attributes the attributes for the supports-screens node.
+ */
+ private void processUsesConfiguration(Attributes attributes) {
+ mManifestData.mUsesConfiguration = new UsesConfiguration();
+
+ mManifestData.mUsesConfiguration.mReqFiveWayNav = getAttributeBooleanValue(
+ attributes,
+ AndroidManifest.ATTRIBUTE_REQ_5WAYNAV, true /*hasNamespace*/);
+ mManifestData.mUsesConfiguration.mReqNavigation = Navigation.getEnum(
+ getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_REQ_NAVIGATION, true /*hasNamespace*/));
+ mManifestData.mUsesConfiguration.mReqHardKeyboard = getAttributeBooleanValue(
+ attributes,
+ AndroidManifest.ATTRIBUTE_REQ_HARDKEYBOARD, true /*hasNamespace*/);
+ mManifestData.mUsesConfiguration.mReqKeyboardType = Keyboard.getEnum(
+ getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_REQ_KEYBOARDTYPE, true /*hasNamespace*/));
+ mManifestData.mUsesConfiguration.mReqTouchScreen = TouchScreen.getEnum(
+ getAttributeValue(attributes,
+ AndroidManifest.ATTRIBUTE_REQ_TOUCHSCREEN, true /*hasNamespace*/));
+ }
+
+ /**
+ * Searches through the attributes list for a particular one and returns its value.
+ * @param attributes the attribute list to search through
+ * @param attributeName the name of the attribute to look for.
+ * @param hasNamespace Indicates whether the attribute has an android namespace.
+ * @return a String with the value or null if the attribute was not found.
+ * @see SdkConstants#NS_RESOURCES
+ */
+ private String getAttributeValue(Attributes attributes, String attributeName,
+ boolean hasNamespace) {
+ int count = attributes.getLength();
+ for (int i = 0 ; i < count ; i++) {
+ if (attributeName.equals(attributes.getLocalName(i)) &&
+ ((hasNamespace &&
+ SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) ||
+ (hasNamespace == false && attributes.getURI(i).length() == 0))) {
+ return attributes.getValue(i);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Searches through the attributes list for a particular one and returns its value as a
+ * Boolean. If the attribute is not present, this will return null.
+ * @param attributes the attribute list to search through
+ * @param attributeName the name of the attribute to look for.
+ * @param hasNamespace Indicates whether the attribute has an android namespace.
+ * @return a String with the value or null if the attribute was not found.
+ * @see SdkConstants#NS_RESOURCES
+ */
+ private Boolean getAttributeBooleanValue(Attributes attributes, String attributeName,
+ boolean hasNamespace) {
+ int count = attributes.getLength();
+ for (int i = 0 ; i < count ; i++) {
+ if (attributeName.equals(attributes.getLocalName(i)) &&
+ ((hasNamespace &&
+ SdkConstants.NS_RESOURCES.equals(attributes.getURI(i))) ||
+ (hasNamespace == false && attributes.getURI(i).length() == 0))) {
+ String attr = attributes.getValue(i);
+ if (attr != null) {
+ return Boolean.valueOf(attr);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ }
+
+ private final static SAXParserFactory sParserFactory;
+
+ static {
+ sParserFactory = SAXParserFactory.newInstance();
+ sParserFactory.setNamespaceAware(true);
+ }
+
+ /**
+ * Parses the Android Manifest, and returns a {@link ManifestData} object containing the
+ * result of the parsing.
+ *
+ * @param manifestFile the {@link IAbstractFile} representing the manifest file.
+ * @param gatherData indicates whether the parsing will extract data from the manifest. If false
+ * the method will always return null.
+ * @param errorHandler an optional errorHandler.
+ * @return A class containing the manifest info obtained during the parsing, or null on error.
+ *
+ * @throws StreamException
+ * @throws IOException
+ * @throws SAXException
+ * @throws ParserConfigurationException
+ */
+ public static ManifestData parse(
+ IAbstractFile manifestFile,
+ boolean gatherData,
+ ManifestErrorHandler errorHandler)
+ throws SAXException, IOException, StreamException, ParserConfigurationException {
+ if (manifestFile != null) {
+ SAXParser parser = sParserFactory.newSAXParser();
+
+ ManifestData data = null;
+ if (gatherData) {
+ data = new ManifestData();
+ }
+
+ ManifestHandler manifestHandler = new ManifestHandler(manifestFile,
+ data, errorHandler);
+ parser.parse(new InputSource(manifestFile.getContents()), manifestHandler);
+
+ return data;
+ }
+
+ return null;
+ }
+
+ /**
+ * Parses the Android Manifest, and returns an object containing the result of the parsing.
+ *
+ * <p/>
+ * This is the equivalent of calling <pre>parse(manifestFile, true, null)</pre>
+ *
+ * @param manifestFile the manifest file to parse.
+ *
+ * @throws ParserConfigurationException
+ * @throws StreamException
+ * @throws IOException
+ * @throws SAXException
+ */
+ public static ManifestData parse(IAbstractFile manifestFile)
+ throws SAXException, IOException, StreamException, ParserConfigurationException {
+ return parse(manifestFile, true, null);
+ }
+
+ public static ManifestData parse(IAbstractFolder projectFolder)
+ throws SAXException, IOException, StreamException, ParserConfigurationException {
+ IAbstractFile manifestFile = AndroidManifest.getManifest(projectFolder);
+ if (manifestFile == null) {
+ throw new FileNotFoundException();
+ }
+
+ return parse(manifestFile, true, null);
+ }
+
+ /**
+ * Parses the Android Manifest from an {@link InputStream}, and returns a {@link ManifestData}
+ * object containing the result of the parsing.
+ *
+ * @param manifestFileStream the {@link InputStream} representing the manifest file.
+ * @return A class containing the manifest info obtained during the parsing or null on error.
+ *
+ * @throws StreamException
+ * @throws IOException
+ * @throws SAXException
+ * @throws ParserConfigurationException
+ */
+ public static ManifestData parse(InputStream manifestFileStream)
+ throws SAXException, IOException, StreamException, ParserConfigurationException {
+ if (manifestFileStream != null) {
+ SAXParser parser = sParserFactory.newSAXParser();
+
+ ManifestData data = new ManifestData();
+
+ ManifestHandler manifestHandler = new ManifestHandler(null, data, null);
+ parser.parse(new InputSource(manifestFileStream), manifestHandler);
+
+ return data;
+ }
+
+ return null;
+ }
+}
diff --git a/ide_common/src/com/android/ide/common/xml/ManifestData.java b/ide_common/src/com/android/ide/common/xml/ManifestData.java
new file mode 100644
index 0000000..9b68d60
--- /dev/null
+++ b/ide_common/src/com/android/ide/common/xml/ManifestData.java
@@ -0,0 +1,747 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.xml;
+
+import com.android.resources.Keyboard;
+import com.android.resources.Navigation;
+import com.android.resources.TouchScreen;
+
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Class containing the manifest info obtained during the parsing.
+ */
+public final class ManifestData {
+
+ /**
+ * Value returned by {@link #getMinSdkVersion()} when the value of the minSdkVersion attribute
+ * in the manifest is a codename and not an integer value.
+ */
+ public final static int MIN_SDK_CODENAME = 0;
+
+ /**
+ * Value returned by {@link #getGlEsVersion()} when there are no <uses-feature> node with the
+ * attribute glEsVersion set.
+ */
+ public final static int GL_ES_VERSION_NOT_SET = -1;
+
+ /** Application package */
+ String mPackage;
+ /** Application version Code, null if the attribute is not present. */
+ Integer mVersionCode = null;
+ /** List of all activities */
+ final ArrayList<Activity> mActivities = new ArrayList<Activity>();
+ /** Launcher activity */
+ Activity mLauncherActivity = null;
+ /** list of process names declared by the manifest */
+ Set<String> mProcesses = null;
+ /** debuggable attribute value. If null, the attribute is not present. */
+ Boolean mDebuggable = null;
+ /** API level requirement. if null the attribute was not present. */
+ private String mMinSdkVersionString = null;
+ /** API level requirement. Default is 1 even if missing. If value is a codename, then it'll be
+ * 0 instead. */
+ private int mMinSdkVersion = 1;
+ private int mTargetSdkVersion = 0;
+ /** List of all instrumentations declared by the manifest */
+ final ArrayList<Instrumentation> mInstrumentations =
+ new ArrayList<Instrumentation>();
+ /** List of all libraries in use declared by the manifest */
+ final ArrayList<UsesLibrary> mLibraries = new ArrayList<UsesLibrary>();
+ /** List of all feature in use declared by the manifest */
+ final ArrayList<UsesFeature> mFeatures = new ArrayList<UsesFeature>();
+
+ SupportsScreens mSupportsScreensFromManifest;
+ SupportsScreens mSupportsScreensValues;
+ UsesConfiguration mUsesConfiguration;
+
+ /**
+ * Instrumentation info obtained from manifest
+ */
+ public final static class Instrumentation {
+ private final String mName;
+ private final String mTargetPackage;
+
+ Instrumentation(String name, String targetPackage) {
+ mName = name;
+ mTargetPackage = targetPackage;
+ }
+
+ /**
+ * Returns the fully qualified instrumentation class name
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the Android app package that is the target of this instrumentation
+ */
+ public String getTargetPackage() {
+ return mTargetPackage;
+ }
+ }
+
+ /**
+ * Activity info obtained from the manifest.
+ */
+ public final static class Activity {
+ private final String mName;
+ private final boolean mIsExported;
+ private boolean mHasAction = false;
+ private boolean mHasMainAction = false;
+ private boolean mHasLauncherCategory = false;
+
+ public Activity(String name, boolean exported) {
+ mName = name;
+ mIsExported = exported;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean isExported() {
+ return mIsExported;
+ }
+
+ public boolean hasAction() {
+ return mHasAction;
+ }
+
+ public boolean isHomeActivity() {
+ return mHasMainAction && mHasLauncherCategory;
+ }
+
+ void setHasAction(boolean hasAction) {
+ mHasAction = hasAction;
+ }
+
+ /** If the activity doesn't yet have a filter set for the launcher, this resets both
+ * flags. This is to handle multiple intent-filters where one could have the valid
+ * action, and another one of the valid category.
+ */
+ void resetIntentFilter() {
+ if (isHomeActivity() == false) {
+ mHasMainAction = mHasLauncherCategory = false;
+ }
+ }
+
+ void setHasMainAction(boolean hasMainAction) {
+ mHasMainAction = hasMainAction;
+ }
+
+ void setHasLauncherCategory(boolean hasLauncherCategory) {
+ mHasLauncherCategory = hasLauncherCategory;
+ }
+ }
+
+ /**
+ * Class representing the <code>supports-screens</code> node in the manifest.
+ * By default, all the getters will return null if there was no value defined in the manifest.
+ *
+ * To get an instance with all the actual values, use {@link #resolveSupportsScreensValues(int)}
+ */
+ public final static class SupportsScreens {
+ private Boolean mResizeable;
+ private Boolean mAnyDensity;
+ private Boolean mSmallScreens;
+ private Boolean mNormalScreens;
+ private Boolean mLargeScreens;
+
+ public SupportsScreens() {
+ }
+
+ /**
+ * Instantiate an instance from a string. The string must have been created with
+ * {@link #getEncodedValues()}.
+ * @param value the string.
+ */
+ public SupportsScreens(String value) {
+ String[] values = value.split("\\|");
+
+ mAnyDensity = Boolean.valueOf(values[0]);
+ mResizeable = Boolean.valueOf(values[1]);
+ mSmallScreens = Boolean.valueOf(values[2]);
+ mNormalScreens = Boolean.valueOf(values[3]);
+ mLargeScreens = Boolean.valueOf(values[4]);
+ }
+
+ /**
+ * Returns an instance of {@link SupportsScreens} initialized with the default values
+ * based on the given targetSdkVersion.
+ * @param targetSdkVersion
+ */
+ public static SupportsScreens getDefaultValues(int targetSdkVersion) {
+ SupportsScreens result = new SupportsScreens();
+
+ result.mNormalScreens = Boolean.TRUE;
+ // Screen size and density became available in Android 1.5/API3, so before that
+ // non normal screens were not supported by default. After they are considered
+ // supported.
+ result.mResizeable = result.mAnyDensity = result.mSmallScreens = result.mLargeScreens =
+ targetSdkVersion <= 3 ? Boolean.FALSE : Boolean.TRUE;
+
+ return result;
+ }
+
+ /**
+ * Returns a version of the receiver for which all values have been set, even if they
+ * were not present in the manifest.
+ * @param targetSdkVersion the target api level of the app, since this has an effect
+ * on default values.
+ */
+ public SupportsScreens resolveSupportsScreensValues(int targetSdkVersion) {
+ SupportsScreens result = getDefaultValues(targetSdkVersion);
+
+ // Override the default with the existing values:
+ if (mResizeable != null) result.mResizeable = mResizeable;
+ if (mAnyDensity != null) result.mAnyDensity = mAnyDensity;
+ if (mSmallScreens != null) result.mSmallScreens = mSmallScreens;
+ if (mNormalScreens != null) result.mNormalScreens = mNormalScreens;
+ if (mLargeScreens != null) result.mLargeScreens = mLargeScreens;
+
+ return result;
+ }
+
+ /**
+ * returns the value of the <code>resizeable</code> attribute or null if not present.
+ */
+ public Boolean getResizeable() {
+ return mResizeable;
+ }
+
+ void setResizeable(Boolean resizeable) {
+ mResizeable = getConstantBoolean(resizeable);
+ }
+
+ /**
+ * returns the value of the <code>anyDensity</code> attribute or null if not present.
+ */
+ public Boolean getAnyDensity() {
+ return mAnyDensity;
+ }
+
+ void setAnyDensity(Boolean anyDensity) {
+ mAnyDensity = getConstantBoolean(anyDensity);
+ }
+
+ /**
+ * returns the value of the <code>smallScreens</code> attribute or null if not present.
+ */
+ public Boolean getSmallScreens() {
+ return mSmallScreens;
+ }
+
+ void setSmallScreens(Boolean smallScreens) {
+ mSmallScreens = getConstantBoolean(smallScreens);
+ }
+
+ /**
+ * returns the value of the <code>normalScreens</code> attribute or null if not present.
+ */
+ public Boolean getNormalScreens() {
+ return mNormalScreens;
+ }
+
+ void setNormalScreens(Boolean normalScreens) {
+ mNormalScreens = getConstantBoolean(normalScreens);
+ }
+
+ /**
+ * returns the value of the <code>largeScreens</code> attribute or null if not present.
+ */
+ public Boolean getLargeScreens() {
+ return mLargeScreens;
+ }
+
+ void setLargeScreens(Boolean largeScreens) {
+ mLargeScreens = getConstantBoolean(largeScreens);
+ }
+
+ /**
+ * Returns either {@link Boolean#TRUE} or {@link Boolean#FALSE} based on the value of
+ * the given Boolean object.
+ */
+ private Boolean getConstantBoolean(Boolean v) {
+ if (v != null) {
+ if (v.equals(Boolean.TRUE)) {
+ return Boolean.TRUE;
+ } else {
+ return Boolean.FALSE;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof SupportsScreens) {
+ SupportsScreens support = (SupportsScreens) obj;
+ // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE
+ // (or null), we can simply check they are identical and not bother with
+ // calling equals (which would require to check != null.
+ // see #getConstanntBoolean(Boolean)
+ return mResizeable == support.mResizeable &&
+ mAnyDensity == support.mAnyDensity &&
+ mSmallScreens == support.mSmallScreens &&
+ mNormalScreens == support.mNormalScreens &&
+ mLargeScreens == support.mLargeScreens;
+ }
+
+ return false;
+ }
+
+ /* Override hashCode, mostly to make Eclipse happy and not warn about it.
+ * And if you ever put this in a Map or Set, it will avoid surprises. */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mAnyDensity == null) ? 0 : mAnyDensity.hashCode());
+ result = prime * result + ((mLargeScreens == null) ? 0 : mLargeScreens.hashCode());
+ result = prime * result + ((mNormalScreens == null) ? 0 : mNormalScreens.hashCode());
+ result = prime * result + ((mResizeable == null) ? 0 : mResizeable.hashCode());
+ result = prime * result + ((mSmallScreens == null) ? 0 : mSmallScreens.hashCode());
+ return result;
+ }
+
+ /**
+ * Returns true if the two instances support the same screen sizes.
+ * This is similar to {@link #equals(Object)} except that it ignores the values of
+ * {@link #getAnyDensity()} and {@link #getResizeable()}.
+ * @param support the other instance to compare to.
+ * @return true if the two instances support the same screen sizes.
+ */
+ public boolean hasSameScreenSupportAs(SupportsScreens support) {
+ // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE
+ // (or null), we can simply check they are identical and not bother with
+ // calling equals (which would require to check != null.
+ // see #getConstanntBoolean(Boolean)
+
+ // This only checks that matter here are the screen sizes. resizeable and anyDensity
+ // are not checked.
+ return mSmallScreens == support.mSmallScreens &&
+ mNormalScreens == support.mNormalScreens &&
+ mLargeScreens == support.mLargeScreens;
+ }
+
+ /**
+ * Returns true if the two instances have strictly different screen size support.
+ * This means that there is no screen size that they both support.
+ * @param support the other instance to compare to.
+ * @return true if they are stricly different.
+ */
+ public boolean hasStrictlyDifferentScreenSupportAs(SupportsScreens support) {
+ // since all the fields are guaranteed to be either Boolean.TRUE or Boolean.FALSE
+ // (or null), we can simply check they are identical and not bother with
+ // calling equals (which would require to check != null.
+ // see #getConstanntBoolean(Boolean)
+
+ // This only checks that matter here are the screen sizes. resizeable and anyDensity
+ // are not checked.
+ return (mSmallScreens != Boolean.TRUE || support.mSmallScreens != Boolean.TRUE) &&
+ (mNormalScreens != Boolean.TRUE || support.mNormalScreens != Boolean.TRUE) &&
+ (mLargeScreens != Boolean.TRUE || support.mLargeScreens != Boolean.TRUE);
+ }
+
+ /**
+ * Comparison of 2 Supports-screens. This only uses screen sizes (ignores resizeable and
+ * anyDensity), and considers that
+ * {@link #hasStrictlyDifferentScreenSupportAs(SupportsScreens)} returns true and
+ * {@link #overlapWith(SupportsScreens)} returns false.
+ * @throws IllegalArgumentException if the two instanced are not strictly different or
+ * overlap each other
+ * @see #hasStrictlyDifferentScreenSupportAs(SupportsScreens)
+ * @see #overlapWith(SupportsScreens)
+ */
+ public int compareScreenSizesWith(SupportsScreens o) {
+ if (hasStrictlyDifferentScreenSupportAs(o) == false) {
+ throw new IllegalArgumentException("The two instances are not strictly different.");
+ }
+ if (overlapWith(o)) {
+ throw new IllegalArgumentException("The two instances overlap each other.");
+ }
+
+ int comp = mLargeScreens.compareTo(o.mLargeScreens);
+ if (comp != 0) return comp;
+
+ comp = mNormalScreens.compareTo(o.mNormalScreens);
+ if (comp != 0) return comp;
+
+ comp = mSmallScreens.compareTo(o.mSmallScreens);
+ if (comp != 0) return comp;
+
+ return 0;
+ }
+
+ /**
+ * Returns a string encoding of the content of the instance. This string can be used to
+ * instantiate a {@link SupportsScreens} object through
+ * {@link #SupportsScreens(String)}.
+ */
+ public String getEncodedValues() {
+ return String.format("%1$s|%2$s|%3$s|%4$s|%5$s",
+ mAnyDensity, mResizeable, mSmallScreens, mNormalScreens, mLargeScreens);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ boolean alreadyOutputSomething = false;
+
+ if (Boolean.TRUE.equals(mSmallScreens)) {
+ alreadyOutputSomething = true;
+ sb.append("small");
+ }
+
+ if (Boolean.TRUE.equals(mNormalScreens)) {
+ if (alreadyOutputSomething) {
+ sb.append(", ");
+ }
+ alreadyOutputSomething = true;
+ sb.append("normal");
+ }
+
+ if (Boolean.TRUE.equals(mLargeScreens)) {
+ if (alreadyOutputSomething) {
+ sb.append(", ");
+ }
+ alreadyOutputSomething = true;
+ sb.append("large");
+ }
+
+ if (alreadyOutputSomething == false) {
+ sb.append("<none>");
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Returns true if the two instance overlap with each other.
+ * This can happen if one instances supports a size, when the other instance doesn't while
+ * supporting a size above and a size below.
+ * @param otherSS the other supports-screens to compare to.
+ */
+ public boolean overlapWith(SupportsScreens otherSS) {
+ if (mSmallScreens == null || mNormalScreens == null || mLargeScreens == null ||
+ otherSS.mSmallScreens == null || otherSS.mNormalScreens == null ||
+ otherSS.mLargeScreens == null) {
+ throw new IllegalArgumentException("Some screen sizes Boolean are not initialized");
+ }
+
+ if (mSmallScreens == Boolean.TRUE && mNormalScreens == Boolean.FALSE &&
+ mLargeScreens == Boolean.TRUE) {
+ return otherSS.mNormalScreens == Boolean.TRUE;
+ }
+
+ if (otherSS.mSmallScreens == Boolean.TRUE && otherSS.mNormalScreens == Boolean.FALSE &&
+ otherSS.mLargeScreens == Boolean.TRUE) {
+ return mNormalScreens == Boolean.TRUE;
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Class representing a <code>uses-library</code> node in the manifest.
+ */
+ public final static class UsesLibrary {
+ String mName;
+ Boolean mRequired = Boolean.TRUE; // default is true even if missing
+
+ public String getName() {
+ return mName;
+ }
+
+ public Boolean getRequired() {
+ return mRequired;
+ }
+ }
+
+ /**
+ * Class representing a <code>uses-feature</code> node in the manifest.
+ */
+ public final static class UsesFeature {
+ String mName;
+ int mGlEsVersion = 0;
+ Boolean mRequired = Boolean.TRUE; // default is true even if missing
+
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the value of the glEsVersion attribute, or 0 if the attribute was not present.
+ */
+ public int getGlEsVersion() {
+ return mGlEsVersion;
+ }
+
+ public Boolean getRequired() {
+ return mRequired;
+ }
+ }
+
+ /**
+ * Class representing the <code>uses-configuration</code> node in the manifest.
+ */
+ public final static class UsesConfiguration {
+ Boolean mReqFiveWayNav;
+ Boolean mReqHardKeyboard;
+ Keyboard mReqKeyboardType;
+ TouchScreen mReqTouchScreen;
+ Navigation mReqNavigation;
+
+ /**
+ * returns the value of the <code>reqFiveWayNav</code> attribute or null if not present.
+ */
+ public Boolean getReqFiveWayNav() {
+ return mReqFiveWayNav;
+ }
+
+ /**
+ * returns the value of the <code>reqNavigation</code> attribute or null if not present.
+ */
+ public Navigation getReqNavigation() {
+ return mReqNavigation;
+ }
+
+ /**
+ * returns the value of the <code>reqHardKeyboard</code> attribute or null if not present.
+ */
+ public Boolean getReqHardKeyboard() {
+ return mReqHardKeyboard;
+ }
+
+ /**
+ * returns the value of the <code>reqKeyboardType</code> attribute or null if not present.
+ */
+ public Keyboard getReqKeyboardType() {
+ return mReqKeyboardType;
+ }
+
+ /**
+ * returns the value of the <code>reqTouchScreen</code> attribute or null if not present.
+ */
+ public TouchScreen getReqTouchScreen() {
+ return mReqTouchScreen;
+ }
+ }
+
+ /**
+ * Returns the package defined in the manifest, if found.
+ * @return The package name or null if not found.
+ */
+ public String getPackage() {
+ return mPackage;
+ }
+
+ /**
+ * Returns the versionCode value defined in the manifest, if found, null otherwise.
+ * @return the versionCode or null if not found.
+ */
+ public Integer getVersionCode() {
+ return mVersionCode;
+ }
+
+ /**
+ * Returns the list of activities found in the manifest.
+ * @return An array of fully qualified class names, or empty if no activity were found.
+ */
+ public Activity[] getActivities() {
+ return mActivities.toArray(new Activity[mActivities.size()]);
+ }
+
+ /**
+ * Returns the name of one activity found in the manifest, that is configured to show
+ * up in the HOME screen.
+ * @return the fully qualified name of a HOME activity or null if none were found.
+ */
+ public Activity getLauncherActivity() {
+ return mLauncherActivity;
+ }
+
+ /**
+ * Returns the list of process names declared by the manifest.
+ */
+ public String[] getProcesses() {
+ if (mProcesses != null) {
+ return mProcesses.toArray(new String[mProcesses.size()]);
+ }
+
+ return new String[0];
+ }
+
+ /**
+ * Returns the <code>debuggable</code> attribute value or null if it is not set.
+ */
+ public Boolean getDebuggable() {
+ return mDebuggable;
+ }
+
+ /**
+ * Returns the <code>minSdkVersion</code> attribute, or null if it's not set.
+ */
+ public String getMinSdkVersionString() {
+ return mMinSdkVersionString;
+ }
+
+ /**
+ * Sets the value of the <code>minSdkVersion</code> attribute.
+ * @param minSdkVersion the string value of the attribute in the manifest.
+ */
+ public void setMinSdkVersionString(String minSdkVersion) {
+ mMinSdkVersionString = minSdkVersion;
+ if (mMinSdkVersionString != null) {
+ try {
+ mMinSdkVersion = Integer.parseInt(mMinSdkVersionString);
+ } catch (NumberFormatException e) {
+ mMinSdkVersion = MIN_SDK_CODENAME;
+ }
+ }
+ }
+
+ /**
+ * Returns the <code>minSdkVersion</code> attribute, or 0 if it's not set or is a codename.
+ * @see #getMinSdkVersionString()
+ */
+ public int getMinSdkVersion() {
+ return mMinSdkVersion;
+ }
+
+
+ /**
+ * Sets the value of the <code>minSdkVersion</code> attribute.
+ * @param targetSdkVersion the string value of the attribute in the manifest.
+ */
+ public void setTargetSdkVersionString(String targetSdkVersion) {
+ if (targetSdkVersion != null) {
+ try {
+ mTargetSdkVersion = Integer.parseInt(targetSdkVersion);
+ } catch (NumberFormatException e) {
+ // keep the value at 0.
+ }
+ }
+ }
+
+ /**
+ * Returns the <code>targetSdkVersion</code> attribute, or the same value as
+ * {@link #getMinSdkVersion()} if it was not set in the manifest.
+ */
+ public int getTargetSdkVersion() {
+ if (mTargetSdkVersion == 0) {
+ return getMinSdkVersion();
+ }
+
+ return mTargetSdkVersion;
+ }
+
+ /**
+ * Returns the list of instrumentations found in the manifest.
+ * @return An array of {@link Instrumentation}, or empty if no instrumentations were
+ * found.
+ */
+ public Instrumentation[] getInstrumentations() {
+ return mInstrumentations.toArray(new Instrumentation[mInstrumentations.size()]);
+ }
+
+ /**
+ * Returns the list of libraries in use found in the manifest.
+ * @return An array of {@link UsesLibrary} objects, or empty if no libraries were found.
+ */
+ public UsesLibrary[] getUsesLibraries() {
+ return mLibraries.toArray(new UsesLibrary[mLibraries.size()]);
+ }
+
+ /**
+ * Returns the list of features in use found in the manifest.
+ * @return An array of {@link UsesFeature} objects, or empty if no libraries were found.
+ */
+ public UsesFeature[] getUsesFeatures() {
+ return mFeatures.toArray(new UsesFeature[mFeatures.size()]);
+ }
+
+ /**
+ * Returns the glEsVersion from a <uses-feature> or {@link #GL_ES_VERSION_NOT_SET} if not set.
+ */
+ public int getGlEsVersion() {
+ for (UsesFeature feature : mFeatures) {
+ if (feature.mGlEsVersion > 0) {
+ return feature.mGlEsVersion;
+ }
+ }
+ return GL_ES_VERSION_NOT_SET;
+ }
+
+ /**
+ * Returns the {@link SupportsScreens} object representing the <code>supports-screens</code>
+ * node, or null if the node doesn't exist at all.
+ * Some values in the {@link SupportsScreens} instance maybe null, indicating that they
+ * were not present in the manifest. To get an instance that contains the values, as seen
+ * by the Android platform when the app is running, use {@link #getSupportsScreensValues()}.
+ */
+ public SupportsScreens getSupportsScreensFromManifest() {
+ return mSupportsScreensFromManifest;
+ }
+
+ /**
+ * Returns an always non-null instance of {@link SupportsScreens} that's been initialized with
+ * the default values, and the values from the manifest.
+ * The default values depends on the manifest values for minSdkVersion and targetSdkVersion.
+ */
+ public synchronized SupportsScreens getSupportsScreensValues() {
+ if (mSupportsScreensValues == null) {
+ if (mSupportsScreensFromManifest == null) {
+ mSupportsScreensValues = SupportsScreens.getDefaultValues(getTargetSdkVersion());
+ } else {
+ // get a SupportsScreen that replace the missing values with default values.
+ mSupportsScreensValues = mSupportsScreensFromManifest.resolveSupportsScreensValues(
+ getTargetSdkVersion());
+ }
+ }
+
+ return mSupportsScreensValues;
+ }
+
+ /**
+ * Returns the {@link UsesConfiguration} object representing the <code>uses-configuration</code>
+ * node, or null if the node doesn't exist at all.
+ */
+ public UsesConfiguration getUsesConfiguration() {
+ return mUsesConfiguration;
+ }
+
+ void addProcessName(String processName) {
+ if (mProcesses == null) {
+ mProcesses = new TreeSet<String>();
+ }
+
+ if (processName.startsWith(":")) {
+ mProcesses.add(mPackage + processName);
+ } else {
+ mProcesses.add(processName);
+ }
+ }
+
+}