• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
getManifest(IAbstractFolder projectFolder)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      */
getPackage(IAbstractFolder projectFolder)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      */
getPackage(IAbstractFile manifestFile)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      */
getDebuggable(IAbstractFile manifestFile)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      */
getVersionCode(IAbstractFile manifestFile)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      */
hasVersionCode(IAbstractFile manifestFile)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      */
getMinSdkVersion(IAbstractFile manifestFile)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      */
getTargetSdkVersion(IAbstractFile manifestFile)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      */
getApplicationIcon(IAbstractFile manifestFile)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      */
getApplicationLabel(IAbstractFile manifestFile)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      */
combinePackageAndClassName(String javaPackage, String className)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      */
extractActivityName(String fullActivityName, String packageName)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