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