OSDN Git Service

Add proper support for <uses-library> and <uses-feature> in the manifest parsing.
authorXavier Ducrohet <xav@android.com>
Wed, 19 May 2010 02:12:21 +0000 (19:12 -0700)
committerXavier Ducrohet <xav@android.com>
Mon, 24 May 2010 22:49:43 +0000 (15:49 -0700)
Change-Id: I80d5fd57f66005fcb4157cb6acc3f9473dd19701

eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/junit/InstrumentationRunnerValidator.java
sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifest.java
sdkmanager/libs/sdklib/src/com/android/sdklib/xml/AndroidManifestParser.java
sdkmanager/libs/sdklib/src/com/android/sdklib/xml/ManifestData.java
sdkmanager/libs/sdklib/tests/com/android/sdklib/testdata/AndroidManifest-testapp.xml
sdkmanager/libs/sdklib/tests/com/android/sdklib/xml/AndroidManifestParserTest.java

index 63824e5..5c88cf2 100644 (file)
@@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
 import com.android.sdklib.SdkConstants;
 import com.android.sdklib.xml.ManifestData;
 import com.android.sdklib.xml.ManifestData.Instrumentation;
+import com.android.sdklib.xml.ManifestData.UsesLibrary;
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.CoreException;
@@ -86,8 +87,8 @@ class InstrumentationRunnerValidator {
      * @return true if test runner library found, false otherwise
      */
     private boolean hasTestRunnerLibrary(ManifestData manifestData) {
-       for (String lib : manifestData.getUsesLibraries()) {
-           if (lib.equals(AndroidConstants.LIBRARY_TEST_RUNNER)) {
+       for (UsesLibrary lib : manifestData.getUsesLibraries()) {
+           if (AndroidConstants.LIBRARY_TEST_RUNNER.equals(lib.getName())) {
                return true;
            }
        }
index f137928..476418d 100644 (file)
@@ -48,10 +48,13 @@ public final class AndroidManifest {
     public final static String NODE_USES_LIBRARY = "uses-library";
     public final static String NODE_SUPPORTS_SCREENS = "supports-screens";
     public final static String NODE_USES_CONFIGURATION = "uses-configuration";
+    public final static String NODE_USES_FEATURE = "uses-feature";
 
     public final static String ATTRIBUTE_PACKAGE = "package";
     public final static String ATTRIBUTE_VERSIONCODE = "versionCode";
     public final static String ATTRIBUTE_NAME = "name";
+    public final static String ATTRIBUTE_REQUIRED = "required";
+    public final static String ATTRIBUTE_GLESVERSION = "glEsVersion";
     public final static String ATTRIBUTE_PROCESS = "process";
     public final static String ATTRIBUTE_DEBUGGABLE = "debuggable";
     public final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion";
index e1c6aa4..5d65356 100644 (file)
@@ -27,6 +27,8 @@ import com.android.sdklib.xml.ManifestData.Activity;
 import com.android.sdklib.xml.ManifestData.Instrumentation;
 import com.android.sdklib.xml.ManifestData.SupportsScreens;
 import com.android.sdklib.xml.ManifestData.UsesConfiguration;
+import com.android.sdklib.xml.ManifestData.UsesFeature;
+import com.android.sdklib.xml.ManifestData.UsesLibrary;
 
 import org.xml.sax.Attributes;
 import org.xml.sax.ErrorHandler;
@@ -46,11 +48,11 @@ import javax.xml.parsers.SAXParserFactory;
 
 public class AndroidManifestParser {
 
-    private final static int LEVEL_MANIFEST = 0;
-    private final static int LEVEL_APPLICATION = 1;
-    private final static int LEVEL_ACTIVITY = 2;
-    private final static int LEVEL_INTENT_FILTER = 3;
-    private final static int LEVEL_CATEGORY = 4;
+    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$
@@ -134,7 +136,7 @@ public class AndroidManifestParser {
                 if (mValidLevel == mCurrentLevel) {
                     String value;
                     switch (mValidLevel) {
-                        case LEVEL_MANIFEST:
+                        case LEVEL_TOP:
                             if (AndroidManifest.NODE_MANIFEST.equals(localName)) {
                                 // lets get the package name.
                                 mManifestData.mPackage = getAttributeValue(attributes,
@@ -154,7 +156,7 @@ public class AndroidManifestParser {
                                 mValidLevel++;
                             }
                             break;
-                        case LEVEL_APPLICATION:
+                        case LEVEL_INSIDE_MANIFEST:
                             if (AndroidManifest.NODE_APPLICATION.equals(localName)) {
                                 value = getAttributeValue(attributes,
                                         AndroidManifest.ATTRIBUTE_PROCESS,
@@ -184,9 +186,46 @@ public class AndroidManifestParser {
                             } 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_ACTIVITY:
+                        case LEVEL_INSIDE_APPLICATION:
                             if (AndroidManifest.NODE_ACTIVITY.equals(localName)) {
                                 processActivityNode(attributes);
                                 mValidLevel++;
@@ -204,11 +243,25 @@ public class AndroidManifestParser {
                                         AndroidManifest.ATTRIBUTE_NAME,
                                         true /* hasNamespace */);
                                 if (value != null) {
-                                    mManifestData.mLibraries.add(value);
+                                    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_INTENT_FILTER:
+                        case LEVEL_INSIDE_APP_COMPONENT:
                             // only process this level if we are in an activity
                             if (mCurrentActivity != null &&
                                     AndroidManifest.NODE_INTENT.equals(localName)) {
@@ -216,7 +269,7 @@ public class AndroidManifestParser {
                                 mValidLevel++;
                             }
                             break;
-                        case LEVEL_CATEGORY:
+                        case LEVEL_INSIDE_INTENT_FILTER:
                             if (mCurrentActivity != null) {
                                 if (AndroidManifest.NODE_ACTION.equals(localName)) {
                                     // get the name attribute
@@ -271,10 +324,10 @@ public class AndroidManifestParser {
                 // process the end of the element
                 if (mValidLevel == mCurrentLevel) {
                     switch (mValidLevel) {
-                        case LEVEL_ACTIVITY:
+                        case LEVEL_INSIDE_APPLICATION:
                             mCurrentActivity = null;
                             break;
-                        case LEVEL_INTENT_FILTER:
+                        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 &&
index 390a1ea..71eaba5 100644 (file)
@@ -46,7 +46,9 @@ public final class ManifestData {
     final ArrayList<Instrumentation> mInstrumentations =
         new ArrayList<Instrumentation>();
     /** List of all libraries in use declared by the manifest */
-    final ArrayList<String> mLibraries = new ArrayList<String>();
+    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 mSupportsScreens;
     UsesConfiguration mUsesConfiguration;
@@ -179,6 +181,46 @@ public final class ManifestData {
     }
 
     /**
+     * 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 {
@@ -293,10 +335,18 @@ public final class ManifestData {
 
     /**
      * Returns the list of libraries in use found in the manifest.
-     * @return An array of library names, or empty if no libraries were found.
+     * @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 String[] getUsesLibraries() {
-        return mLibraries.toArray(new String[mLibraries.size()]);
+    public UsesFeature[] getUsesFeatures() {
+        return mFeatures.toArray(new UsesFeature[mFeatures.size()]);
     }
 
     /**
index cf58934..57d04d8 100644 (file)
@@ -1,28 +1,29 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.testapp"
-    android:versionCode="42"
+    package="com.android.testapp" android:versionCode="42"
     android:versionName="1.42">
-  <application android:icon="@drawable/icon">
-    <activity android:name="com.android.testapp.MainActivity"
-              android:label="@string/app_name">
-      <intent-filter>
-        <action android:name="android.intent.action.MAIN" />
-          <category android:name="android.intent.category.LAUNCHER" />"
-          <category android:name="android.intent.category.DEFAULT" />
-      </intent-filter>
-    </activity>
-    <uses-library android:name="android.test.runner"/>
-  </application>"
-  <uses-sdk android:minSdkVersion="7"/>
-  <supports-screens android:resizeable="true" android:smallScreens="true"
-      android:anyDensity="true" android:largeScreens="true" android:normalScreens="true"/>
-  <uses-configuration android:reqKeyboardType="twelvekey"
-      android:reqTouchScreen="finger" android:reqFiveWayNav="true" android:reqHardKeyboard="true"
-      android:reqNavigation="nonav"/>
-  <uses-feature android:glEsVersion="1"/>
-  <uses-feature android:name="com.foo.feature"/>
-  <instrumentation android:name="android.test.InstrumentationTestRunner"
-                   android:targetPackage="com.example.android.apis"
-                   android:label="Tests for Api Demos."/>
-</manifest>
\ No newline at end of file
+    <application android:icon="@drawable/icon">
+        <activity android:name="com.android.testapp.MainActivity"
+            android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <uses-library android:name="android.test.runner"
+            android:required="false" />
+        <uses-library android:name="android.test.runner2" />
+    </application>
+    <uses-sdk android:minSdkVersion="7" />
+    <supports-screens android:resizeable="true"
+        android:smallScreens="true" android:anyDensity="true"
+        android:largeScreens="true" android:normalScreens="true" />
+    <uses-configuration android:reqKeyboardType="twelvekey"
+        android:reqTouchScreen="finger" android:reqFiveWayNav="true"
+        android:reqHardKeyboard="true" android:reqNavigation="nonav" />
+    <uses-feature android:glEsVersion="0x00020001" />
+    <uses-feature android:name="com.foo.feature" />
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.example.android.apis" android:label="Tests for Api Demos." />
+</manifest>
index 8c69a9c..1936af4 100644 (file)
@@ -19,6 +19,8 @@ package com.android.sdklib.xml;
 import com.android.sdklib.resources.Keyboard;
 import com.android.sdklib.resources.Navigation;
 import com.android.sdklib.resources.TouchScreen;
+import com.android.sdklib.xml.ManifestData.UsesFeature;
+import com.android.sdklib.xml.ManifestData.UsesLibrary;
 
 import java.io.InputStream;
 
@@ -41,6 +43,8 @@ public class AndroidManifestParserTest extends TestCase {
     private static final Integer VERSION_CODE = 42;
     private static final String ACTIVITY_NAME = "com.android.testapp.MainActivity"; //$NON-NLS-1$
     private static final String LIBRARY_NAME = "android.test.runner"; //$NON-NLS-1$
+    private static final String LIBRARY_NAME2 = "android.test.runner2"; //$NON-NLS-1$
+    private static final String FEATURE_NAME = "com.foo.feature"; //$NON-NLS-1$
     private static final String INSTRUMENTATION_NAME = "android.test.InstrumentationTestRunner"; //$NON-NLS-1$
     private static final String INSTRUMENTATION_TARGET = "com.android.AndroidProject"; //$NON-NLS-1$
 
@@ -129,8 +133,23 @@ public class AndroidManifestParserTest extends TestCase {
     }
 
     public void testGetUsesLibraries() {
-        assertEquals(1, mManifestTestApp.getUsesLibraries().length);
-        assertEquals(LIBRARY_NAME, mManifestTestApp.getUsesLibraries()[0]);
+        UsesLibrary[] libraries = mManifestTestApp.getUsesLibraries();
+
+        assertEquals(2,             libraries.length);
+        assertEquals(LIBRARY_NAME,  libraries[0].getName());
+        assertEquals(Boolean.FALSE, libraries[0].getRequired());
+        assertEquals(LIBRARY_NAME2, libraries[1].getName());
+        assertEquals(Boolean.TRUE,  libraries[1].getRequired());
+    }
+
+    public void testGetUsesFeatures() {
+        UsesFeature[] features = mManifestTestApp.getUsesFeatures();
+
+        assertEquals(2,            features.length);
+        assertEquals(0x00020001,   features[0].mGlEsVersion);
+        assertEquals(Boolean.TRUE, features[0].getRequired());
+        assertEquals(FEATURE_NAME, features[1].getName());
+        assertEquals(Boolean.TRUE, features[1].getRequired());
     }
 
     public void testGetPackageName() {