Home | History | Annotate | Download | only in xml
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.sdklib.xml;
     18 
     19 import com.android.io.IAbstractFile;
     20 import com.android.io.IAbstractFolder;
     21 import com.android.io.StreamException;
     22 import com.android.sdklib.SdkConstants;
     23 
     24 import org.w3c.dom.Node;
     25 import org.xml.sax.InputSource;
     26 
     27 import javax.xml.xpath.XPath;
     28 import javax.xml.xpath.XPathConstants;
     29 import javax.xml.xpath.XPathExpressionException;
     30 
     31 /**
     32  * Helper and Constants for the AndroidManifest.xml file.
     33  *
     34  */
     35 public final class AndroidManifest {
     36 
     37     public final static String NODE_MANIFEST = "manifest";
     38     public final static String NODE_APPLICATION = "application";
     39     public final static String NODE_ACTIVITY = "activity";
     40     public final static String NODE_ACTIVITY_ALIAS = "activity-alias";
     41     public final static String NODE_SERVICE = "service";
     42     public final static String NODE_RECEIVER = "receiver";
     43     public final static String NODE_PROVIDER = "provider";
     44     public final static String NODE_INTENT = "intent-filter";
     45     public final static String NODE_ACTION = "action";
     46     public final static String NODE_CATEGORY = "category";
     47     public final static String NODE_USES_SDK = "uses-sdk";
     48     public final static String NODE_INSTRUMENTATION = "instrumentation";
     49     public final static String NODE_USES_LIBRARY = "uses-library";
     50     public final static String NODE_SUPPORTS_SCREENS = "supports-screens";
     51     public final static String NODE_USES_CONFIGURATION = "uses-configuration";
     52     public final static String NODE_USES_FEATURE = "uses-feature";
     53 
     54     public final static String ATTRIBUTE_PACKAGE = "package";
     55     public final static String ATTRIBUTE_VERSIONCODE = "versionCode";
     56     public final static String ATTRIBUTE_NAME = "name";
     57     public final static String ATTRIBUTE_REQUIRED = "required";
     58     public final static String ATTRIBUTE_GLESVERSION = "glEsVersion";
     59     public final static String ATTRIBUTE_PROCESS = "process";
     60     public final static String ATTRIBUTE_DEBUGGABLE = "debuggable";
     61     public final static String ATTRIBUTE_LABEL = "label";
     62     public final static String ATTRIBUTE_ICON = "icon";
     63     public final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion";
     64     public final static String ATTRIBUTE_TARGET_SDK_VERSION = "targetSdkVersion";
     65     public final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage";
     66     public final static String ATTRIBUTE_TARGET_ACTIVITY = "targetActivity";
     67     public final static String ATTRIBUTE_MANAGE_SPACE_ACTIVITY = "manageSpaceActivity";
     68     public final static String ATTRIBUTE_EXPORTED = "exported";
     69     public final static String ATTRIBUTE_RESIZEABLE = "resizeable";
     70     public final static String ATTRIBUTE_ANYDENSITY = "anyDensity";
     71     public final static String ATTRIBUTE_SMALLSCREENS = "smallScreens";
     72     public final static String ATTRIBUTE_NORMALSCREENS = "normalScreens";
     73     public final static String ATTRIBUTE_LARGESCREENS = "largeScreens";
     74     public final static String ATTRIBUTE_REQ_5WAYNAV = "reqFiveWayNav";
     75     public final static String ATTRIBUTE_REQ_NAVIGATION = "reqNavigation";
     76     public final static String ATTRIBUTE_REQ_HARDKEYBOARD = "reqHardKeyboard";
     77     public final static String ATTRIBUTE_REQ_KEYBOARDTYPE = "reqKeyboardType";
     78     public final static String ATTRIBUTE_REQ_TOUCHSCREEN = "reqTouchScreen";
     79     public static final String ATTRIBUTE_THEME = "theme";
     80 
     81     /**
     82      * Returns an {@link IAbstractFile} object representing the manifest for the given project.
     83      *
     84      * @param projectFolder The project containing the manifest file.
     85      * @return An IAbstractFile object pointing to the manifest or null if the manifest
     86      *         is missing.
     87      */
     88     public static IAbstractFile getManifest(IAbstractFolder projectFolder) {
     89         IAbstractFile file = projectFolder.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML);
     90         if (file.exists()) {
     91             return file;
     92         }
     93 
     94         return null;
     95     }
     96 
     97     /**
     98      * Returns the package for a given project.
     99      * @param projectFolder the folder of the project.
    100      * @return the package info or null (or empty) if not found.
    101      * @throws XPathExpressionException
    102      * @throws StreamException If any error happens when reading the manifest.
    103      */
    104     public static String getPackage(IAbstractFolder projectFolder)
    105             throws XPathExpressionException, StreamException {
    106         IAbstractFile file = getManifest(projectFolder);
    107         if (file != null) {
    108             return getPackage(file);
    109         }
    110 
    111         return null;
    112     }
    113 
    114     /**
    115      * Returns the package for a given manifest.
    116      * @param manifestFile the manifest to parse.
    117      * @return the package info or null (or empty) if not found.
    118      * @throws XPathExpressionException
    119      * @throws StreamException If any error happens when reading the manifest.
    120      */
    121     public static String getPackage(IAbstractFile manifestFile)
    122             throws XPathExpressionException, StreamException {
    123         XPath xPath = AndroidXPathFactory.newXPath();
    124 
    125         return xPath.evaluate(
    126                 "/"  + NODE_MANIFEST +
    127                 "/@" + ATTRIBUTE_PACKAGE,
    128                 new InputSource(manifestFile.getContents()));
    129     }
    130 
    131     /**
    132      * Returns whether the manifest is set to make the application debuggable.
    133      *
    134      * If the give manifest does not contain the debuggable attribute then the application
    135      * is considered to not be debuggable.
    136      *
    137      * @param manifestFile the manifest to parse.
    138      * @return true if the application is debuggable.
    139      * @throws XPathExpressionException
    140      * @throws StreamException If any error happens when reading the manifest.
    141      */
    142     public static boolean getDebuggable(IAbstractFile manifestFile)
    143             throws XPathExpressionException, StreamException {
    144         XPath xPath = AndroidXPathFactory.newXPath();
    145 
    146         String value = xPath.evaluate(
    147                 "/"  + NODE_MANIFEST +
    148                 "/"  + NODE_APPLICATION +
    149                 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX +
    150                 ":"  + ATTRIBUTE_DEBUGGABLE,
    151                 new InputSource(manifestFile.getContents()));
    152 
    153         // default is not debuggable, which is the same behavior as parseBoolean
    154         return Boolean.parseBoolean(value);
    155     }
    156 
    157     /**
    158      * Returns the value of the versionCode attribute or -1 if the value is not set.
    159      * @param manifestFile the manifest file to read the attribute from.
    160      * @return the integer value or -1 if not set.
    161      * @throws XPathExpressionException
    162      * @throws StreamException If any error happens when reading the manifest.
    163      */
    164     public static int getVersionCode(IAbstractFile manifestFile)
    165             throws XPathExpressionException, StreamException {
    166         XPath xPath = AndroidXPathFactory.newXPath();
    167 
    168         String result = xPath.evaluate(
    169                 "/"  + NODE_MANIFEST +
    170                 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX +
    171                 ":"  + ATTRIBUTE_VERSIONCODE,
    172                 new InputSource(manifestFile.getContents()));
    173 
    174         try {
    175             return Integer.parseInt(result);
    176         } catch (NumberFormatException e) {
    177             return -1;
    178         }
    179     }
    180 
    181     /**
    182      * Returns whether the version Code attribute is set in a given manifest.
    183      * @param manifestFile the manifest to check
    184      * @return true if the versionCode attribute is present and its value is not empty.
    185      * @throws XPathExpressionException
    186      * @throws StreamException If any error happens when reading the manifest.
    187      */
    188     public static boolean hasVersionCode(IAbstractFile manifestFile)
    189             throws XPathExpressionException, StreamException {
    190         XPath xPath = AndroidXPathFactory.newXPath();
    191 
    192         Object result = xPath.evaluate(
    193                 "/"  + NODE_MANIFEST +
    194                 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX +
    195                 ":"  + ATTRIBUTE_VERSIONCODE,
    196                 new InputSource(manifestFile.getContents()),
    197                 XPathConstants.NODE);
    198 
    199         if (result != null) {
    200             Node node  = (Node)result;
    201             if (node.getNodeValue().length() > 0) {
    202                 return true;
    203             }
    204         }
    205 
    206         return false;
    207     }
    208 
    209     /**
    210      * Returns the value of the minSdkVersion attribute.
    211      * <p/>
    212      * If the attribute is set with an int value, the method returns an Integer object.
    213      * <p/>
    214      * If the attribute is set with a codename, it returns the codename as a String object.
    215      * <p/>
    216      * If the attribute is not set, it returns null.
    217      *
    218      * @param manifestFile the manifest file to read the attribute from.
    219      * @return the attribute value.
    220      * @throws XPathExpressionException
    221      * @throws StreamException If any error happens when reading the manifest.
    222      */
    223     public static Object getMinSdkVersion(IAbstractFile manifestFile)
    224             throws XPathExpressionException, StreamException {
    225         XPath xPath = AndroidXPathFactory.newXPath();
    226 
    227         String result = xPath.evaluate(
    228                 "/"  + NODE_MANIFEST +
    229                 "/"  + NODE_USES_SDK +
    230                 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX +
    231                 ":"  + ATTRIBUTE_MIN_SDK_VERSION,
    232                 new InputSource(manifestFile.getContents()));
    233 
    234         try {
    235             return Integer.valueOf(result);
    236         } catch (NumberFormatException e) {
    237             return result.length() > 0 ? result : null;
    238         }
    239     }
    240 
    241     /**
    242      * Returns the value of the targetSdkVersion attribute (defaults to 1 if the attribute is
    243      * not set), or -1 if the value is a codename.
    244      * @param manifestFile the manifest file to read the attribute from.
    245      * @return the integer value or -1 if not set.
    246      * @throws XPathExpressionException
    247      * @throws StreamException If any error happens when reading the manifest.
    248      */
    249     public static Integer getTargetSdkVersion(IAbstractFile manifestFile)
    250             throws XPathExpressionException, StreamException {
    251         XPath xPath = AndroidXPathFactory.newXPath();
    252 
    253         String result = xPath.evaluate(
    254                 "/"  + NODE_MANIFEST +
    255                 "/"  + NODE_USES_SDK +
    256                 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX +
    257                 ":"  + ATTRIBUTE_TARGET_SDK_VERSION,
    258                 new InputSource(manifestFile.getContents()));
    259 
    260         try {
    261             return Integer.valueOf(result);
    262         } catch (NumberFormatException e) {
    263             return result.length() > 0 ? -1 : null;
    264         }
    265     }
    266 
    267     /**
    268      * Returns the application icon  for a given manifest.
    269      * @param manifestFile the manifest to parse.
    270      * @return the icon or null (or empty) if not found.
    271      * @throws XPathExpressionException
    272      * @throws StreamException If any error happens when reading the manifest.
    273      */
    274     public static String getApplicationIcon(IAbstractFile manifestFile)
    275             throws XPathExpressionException, StreamException {
    276         XPath xPath = AndroidXPathFactory.newXPath();
    277 
    278         return xPath.evaluate(
    279                 "/"  + NODE_MANIFEST +
    280                 "/"  + NODE_APPLICATION +
    281                 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX +
    282                 ":"  + ATTRIBUTE_ICON,
    283                 new InputSource(manifestFile.getContents()));
    284     }
    285 
    286     /**
    287      * Returns the application label  for a given manifest.
    288      * @param manifestFile the manifest to parse.
    289      * @return the label or null (or empty) if not found.
    290      * @throws XPathExpressionException
    291      * @throws StreamException If any error happens when reading the manifest.
    292      */
    293     public static String getApplicationLabel(IAbstractFile manifestFile)
    294             throws XPathExpressionException, StreamException {
    295         XPath xPath = AndroidXPathFactory.newXPath();
    296 
    297         return xPath.evaluate(
    298                 "/"  + NODE_MANIFEST +
    299                 "/"  + NODE_APPLICATION +
    300                 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX +
    301                 ":"  + ATTRIBUTE_LABEL,
    302                 new InputSource(manifestFile.getContents()));
    303     }
    304 
    305     /**
    306      * Combines a java package, with a class value from the manifest to make a fully qualified
    307      * class name
    308      * @param javaPackage the java package from the manifest.
    309      * @param className the class name from the manifest.
    310      * @return the fully qualified class name.
    311      */
    312     public static String combinePackageAndClassName(String javaPackage, String className) {
    313         if (className == null || className.length() == 0) {
    314             return javaPackage;
    315         }
    316         if (javaPackage == null || javaPackage.length() == 0) {
    317             return className;
    318         }
    319 
    320         // the class name can be a subpackage (starts with a '.'
    321         // char), a simple class name (no dot), or a full java package
    322         boolean startWithDot = (className.charAt(0) == '.');
    323         boolean hasDot = (className.indexOf('.') != -1);
    324         if (startWithDot || hasDot == false) {
    325 
    326             // add the concatenation of the package and class name
    327             if (startWithDot) {
    328                 return javaPackage + className;
    329             } else {
    330                 return javaPackage + '.' + className;
    331             }
    332         } else {
    333             // just add the class as it should be a fully qualified java name.
    334             return className;
    335         }
    336     }
    337 
    338     /**
    339      * Given a fully qualified activity name (e.g. com.foo.test.MyClass) and given a project
    340      * package base name (e.g. com.foo), returns the relative activity name that would be used
    341      * the "name" attribute of an "activity" element.
    342      *
    343      * @param fullActivityName a fully qualified activity class name, e.g. "com.foo.test.MyClass"
    344      * @param packageName The project base package name, e.g. "com.foo"
    345      * @return The relative activity name if it can be computed or the original fullActivityName.
    346      */
    347     public static String extractActivityName(String fullActivityName, String packageName) {
    348         if (packageName != null && fullActivityName != null) {
    349             if (packageName.length() > 0 && fullActivityName.startsWith(packageName)) {
    350                 String name = fullActivityName.substring(packageName.length());
    351                 if (name.length() > 0 && name.charAt(0) == '.') {
    352                     return name;
    353                 }
    354             }
    355         }
    356 
    357         return fullActivityName;
    358     }
    359 }
    360