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.sdklib.SdkConstants; 20 import com.android.sdklib.io.IAbstractFile; 21 import com.android.sdklib.io.IAbstractFolder; 22 import com.android.sdklib.io.StreamException; 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_SERVICE = "service"; 41 public final static String NODE_RECEIVER = "receiver"; 42 public final static String NODE_PROVIDER = "provider"; 43 public final static String NODE_INTENT = "intent-filter"; 44 public final static String NODE_ACTION = "action"; 45 public final static String NODE_CATEGORY = "category"; 46 public final static String NODE_USES_SDK = "uses-sdk"; 47 public final static String NODE_INSTRUMENTATION = "instrumentation"; 48 public final static String NODE_USES_LIBRARY = "uses-library"; 49 public final static String NODE_SUPPORTS_SCREENS = "supports-screens"; 50 public final static String NODE_USES_CONFIGURATION = "uses-configuration"; 51 public final static String NODE_USES_FEATURE = "uses-feature"; 52 53 public final static String ATTRIBUTE_PACKAGE = "package"; 54 public final static String ATTRIBUTE_VERSIONCODE = "versionCode"; 55 public final static String ATTRIBUTE_NAME = "name"; 56 public final static String ATTRIBUTE_REQUIRED = "required"; 57 public final static String ATTRIBUTE_GLESVERSION = "glEsVersion"; 58 public final static String ATTRIBUTE_PROCESS = "process"; 59 public final static String ATTRIBUTE_DEBUGGABLE = "debuggable"; 60 public final static String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; 61 public final static String ATTRIBUTE_TARGET_SDK_VERSION = "targetSdkVersion"; 62 public final static String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; 63 public final static String ATTRIBUTE_EXPORTED = "exported"; 64 public final static String ATTRIBUTE_RESIZEABLE = "resizeable"; 65 public final static String ATTRIBUTE_ANYDENSITY = "anyDensity"; 66 public final static String ATTRIBUTE_SMALLSCREENS = "smallScreens"; 67 public final static String ATTRIBUTE_NORMALSCREENS = "normalScreens"; 68 public final static String ATTRIBUTE_LARGESCREENS = "largeScreens"; 69 public final static String ATTRIBUTE_REQ_5WAYNAV = "reqFiveWayNav"; 70 public final static String ATTRIBUTE_REQ_NAVIGATION = "reqNavigation"; 71 public final static String ATTRIBUTE_REQ_HARDKEYBOARD = "reqHardKeyboard"; 72 public final static String ATTRIBUTE_REQ_KEYBOARDTYPE = "reqKeyboardType"; 73 public final static String ATTRIBUTE_REQ_TOUCHSCREEN = "reqTouchScreen"; 74 75 /** 76 * Returns the package for a given project. 77 * @param projectFolder the folder of the project. 78 * @return the package info or null (or empty) if not found. 79 * @throws XPathExpressionException 80 * @throws StreamException If any error happens when reading the manifest. 81 */ 82 public static String getPackage(IAbstractFolder projectFolder) 83 throws XPathExpressionException, StreamException { 84 IAbstractFile file = projectFolder.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML); 85 return getPackage(file); 86 } 87 88 /** 89 * Returns the package for a given manifest. 90 * @param manifestFile the manifest to parse. 91 * @return the package info or null (or empty) if not found. 92 * @throws XPathExpressionException 93 * @throws StreamException If any error happens when reading the manifest. 94 */ 95 public static String getPackage(IAbstractFile manifestFile) 96 throws XPathExpressionException, StreamException { 97 XPath xPath = AndroidXPathFactory.newXPath(); 98 99 return xPath.evaluate( 100 "/" + NODE_MANIFEST + 101 "/@" + ATTRIBUTE_PACKAGE, 102 new InputSource(manifestFile.getContents())); 103 } 104 105 /** 106 * Returns the value of the versionCode attribute or -1 if the value is not set. 107 * @param manifestFile the manifest file to read the attribute from. 108 * @return the integer value or -1 if not set. 109 * @throws XPathExpressionException 110 * @throws StreamException If any error happens when reading the manifest. 111 */ 112 public static int getVersionCode(IAbstractFile manifestFile) 113 throws XPathExpressionException, StreamException { 114 XPath xPath = AndroidXPathFactory.newXPath(); 115 116 String result = xPath.evaluate( 117 "/" + NODE_MANIFEST + 118 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + 119 ":" + ATTRIBUTE_VERSIONCODE, 120 new InputSource(manifestFile.getContents())); 121 122 try { 123 return Integer.parseInt(result); 124 } catch (NumberFormatException e) { 125 return -1; 126 } 127 } 128 129 /** 130 * Returns whether the version Code attribute is set in a given manifest. 131 * @param manifestFile the manifest to check 132 * @return true if the versionCode attribute is present and its value is not empty. 133 * @throws XPathExpressionException 134 * @throws StreamException If any error happens when reading the manifest. 135 */ 136 public static boolean hasVersionCode(IAbstractFile manifestFile) 137 throws XPathExpressionException, StreamException { 138 XPath xPath = AndroidXPathFactory.newXPath(); 139 140 Object result = xPath.evaluate( 141 "/" + NODE_MANIFEST + 142 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + 143 ":" + ATTRIBUTE_VERSIONCODE, 144 new InputSource(manifestFile.getContents()), 145 XPathConstants.NODE); 146 147 if (result != null) { 148 Node node = (Node)result; 149 if (node.getNodeValue().length() > 0) { 150 return true; 151 } 152 } 153 154 return false; 155 } 156 157 /** 158 * Returns the value of the minSdkVersion attribute (defaults to 1 if the attribute is not set), 159 * or -1 if the value is a codename. 160 * @param manifestFile the manifest file to read the attribute from. 161 * @return the integer value or -1 if not set. 162 * @throws XPathExpressionException 163 * @throws StreamException If any error happens when reading the manifest. 164 */ 165 public static int getMinSdkVersion(IAbstractFile manifestFile) 166 throws XPathExpressionException, StreamException { 167 XPath xPath = AndroidXPathFactory.newXPath(); 168 169 String result = xPath.evaluate( 170 "/" + NODE_MANIFEST + 171 "/" + NODE_USES_SDK + 172 "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + 173 ":" + ATTRIBUTE_MIN_SDK_VERSION, 174 new InputSource(manifestFile.getContents())); 175 176 try { 177 return Integer.parseInt(result); 178 } catch (NumberFormatException e) { 179 return result.length() > 0 ? -1 : 1; 180 } 181 } 182 183 184 /** 185 * Combines a java package, with a class value from the manifest to make a fully qualified 186 * class name 187 * @param javaPackage the java package from the manifest. 188 * @param className the class name from the manifest. 189 * @return the fully qualified class name. 190 */ 191 public static String combinePackageAndClassName(String javaPackage, String className) { 192 if (className == null || className.length() == 0) { 193 return javaPackage; 194 } 195 if (javaPackage == null || javaPackage.length() == 0) { 196 return className; 197 } 198 199 // the class name can be a subpackage (starts with a '.' 200 // char), a simple class name (no dot), or a full java package 201 boolean startWithDot = (className.charAt(0) == '.'); 202 boolean hasDot = (className.indexOf('.') != -1); 203 if (startWithDot || hasDot == false) { 204 205 // add the concatenation of the package and class name 206 if (startWithDot) { 207 return javaPackage + className; 208 } else { 209 return javaPackage + '.' + className; 210 } 211 } else { 212 // just add the class as it should be a fully qualified java name. 213 return className; 214 } 215 } 216 217 /** 218 * Given a fully qualified activity name (e.g. com.foo.test.MyClass) and given a project 219 * package base name (e.g. com.foo), returns the relative activity name that would be used 220 * the "name" attribute of an "activity" element. 221 * 222 * @param fullActivityName a fully qualified activity class name, e.g. "com.foo.test.MyClass" 223 * @param packageName The project base package name, e.g. "com.foo" 224 * @return The relative activity name if it can be computed or the original fullActivityName. 225 */ 226 public static String extractActivityName(String fullActivityName, String packageName) { 227 if (packageName != null && fullActivityName != null) { 228 if (packageName.length() > 0 && fullActivityName.startsWith(packageName)) { 229 String name = fullActivityName.substring(packageName.length()); 230 if (name.length() > 0 && name.charAt(0) == '.') { 231 return name; 232 } 233 } 234 } 235 236 return fullActivityName; 237 } 238 } 239