| /* |
| * Copyright (C) 2009 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.xml; |
| |
| import com.android.SdkConstants; |
| import com.android.io.IAbstractFile; |
| import com.android.io.IAbstractFolder; |
| import com.android.io.StreamException; |
| |
| import org.w3c.dom.Node; |
| import org.xml.sax.InputSource; |
| |
| import javax.xml.xpath.XPath; |
| import javax.xml.xpath.XPathConstants; |
| import javax.xml.xpath.XPathExpressionException; |
| |
| /** |
| * Helper and Constants for the AndroidManifest.xml file. |
| * |
| */ |
| public final class AndroidManifest { |
| |
| public static final String NODE_MANIFEST = "manifest"; |
| public static final String NODE_APPLICATION = "application"; |
| public static final String NODE_ACTIVITY = "activity"; |
| public static final String NODE_ACTIVITY_ALIAS = "activity-alias"; |
| public static final String NODE_SERVICE = "service"; |
| public static final String NODE_RECEIVER = "receiver"; |
| public static final String NODE_PROVIDER = "provider"; |
| public static final String NODE_INTENT = "intent-filter"; |
| public static final String NODE_ACTION = "action"; |
| public static final String NODE_CATEGORY = "category"; |
| public static final String NODE_USES_SDK = "uses-sdk"; |
| public static final String NODE_INSTRUMENTATION = "instrumentation"; |
| public static final String NODE_USES_LIBRARY = "uses-library"; |
| public static final String NODE_SUPPORTS_SCREENS = "supports-screens"; |
| public static final String NODE_USES_CONFIGURATION = "uses-configuration"; |
| public static final String NODE_USES_FEATURE = "uses-feature"; |
| |
| public static final String ATTRIBUTE_PACKAGE = "package"; |
| public static final String ATTRIBUTE_VERSIONCODE = "versionCode"; |
| public static final String ATTRIBUTE_NAME = "name"; |
| public static final String ATTRIBUTE_REQUIRED = "required"; |
| public static final String ATTRIBUTE_GLESVERSION = "glEsVersion"; |
| public static final String ATTRIBUTE_PROCESS = "process"; |
| public static final String ATTRIBUTE_DEBUGGABLE = "debuggable"; |
| public static final String ATTRIBUTE_LABEL = "label"; |
| public static final String ATTRIBUTE_ICON = "icon"; |
| public static final String ATTRIBUTE_MIN_SDK_VERSION = "minSdkVersion"; |
| public static final String ATTRIBUTE_TARGET_SDK_VERSION = "targetSdkVersion"; |
| public static final String ATTRIBUTE_TARGET_PACKAGE = "targetPackage"; |
| public static final String ATTRIBUTE_TARGET_ACTIVITY = "targetActivity"; |
| public static final String ATTRIBUTE_MANAGE_SPACE_ACTIVITY = "manageSpaceActivity"; |
| public static final String ATTRIBUTE_EXPORTED = "exported"; |
| public static final String ATTRIBUTE_RESIZEABLE = "resizeable"; |
| public static final String ATTRIBUTE_ANYDENSITY = "anyDensity"; |
| public static final String ATTRIBUTE_SMALLSCREENS = "smallScreens"; |
| public static final String ATTRIBUTE_NORMALSCREENS = "normalScreens"; |
| public static final String ATTRIBUTE_LARGESCREENS = "largeScreens"; |
| public static final String ATTRIBUTE_REQ_5WAYNAV = "reqFiveWayNav"; |
| public static final String ATTRIBUTE_REQ_NAVIGATION = "reqNavigation"; |
| public static final String ATTRIBUTE_REQ_HARDKEYBOARD = "reqHardKeyboard"; |
| public static final String ATTRIBUTE_REQ_KEYBOARDTYPE = "reqKeyboardType"; |
| public static final String ATTRIBUTE_REQ_TOUCHSCREEN = "reqTouchScreen"; |
| public static final String ATTRIBUTE_THEME = "theme"; |
| public static final String ATTRIBUTE_BACKUP_AGENT = "backupAgent"; |
| public static final String ATTRIBUTE_PARENT_ACTIVITY_NAME = "parentActivityName"; |
| |
| /** |
| * Returns an {@link IAbstractFile} object representing the manifest for the given project. |
| * |
| * @param projectFolder The project containing the manifest file. |
| * @return An IAbstractFile object pointing to the manifest or null if the manifest |
| * is missing. |
| */ |
| public static IAbstractFile getManifest(IAbstractFolder projectFolder) { |
| IAbstractFile file = projectFolder.getFile(SdkConstants.FN_ANDROID_MANIFEST_XML); |
| if (file.exists()) { |
| return file; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the package for a given project. |
| * @param projectFolder the folder of the project. |
| * @return the package info or null (or empty) if not found. |
| * @throws XPathExpressionException |
| * @throws StreamException If any error happens when reading the manifest. |
| */ |
| public static String getPackage(IAbstractFolder projectFolder) |
| throws XPathExpressionException, StreamException { |
| IAbstractFile file = getManifest(projectFolder); |
| if (file != null) { |
| return getPackage(file); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the package for a given manifest. |
| * @param manifestFile the manifest to parse. |
| * @return the package info or null (or empty) if not found. |
| * @throws XPathExpressionException |
| * @throws StreamException If any error happens when reading the manifest. |
| */ |
| public static String getPackage(IAbstractFile manifestFile) |
| throws XPathExpressionException, StreamException { |
| XPath xPath = AndroidXPathFactory.newXPath(); |
| |
| return xPath.evaluate( |
| "/" + NODE_MANIFEST + |
| "/@" + ATTRIBUTE_PACKAGE, |
| new InputSource(manifestFile.getContents())); |
| } |
| |
| /** |
| * Returns whether the manifest is set to make the application debuggable. |
| * |
| * If the give manifest does not contain the debuggable attribute then the application |
| * is considered to not be debuggable. |
| * |
| * @param manifestFile the manifest to parse. |
| * @return true if the application is debuggable. |
| * @throws XPathExpressionException |
| * @throws StreamException If any error happens when reading the manifest. |
| */ |
| public static boolean getDebuggable(IAbstractFile manifestFile) |
| throws XPathExpressionException, StreamException { |
| XPath xPath = AndroidXPathFactory.newXPath(); |
| |
| String value = xPath.evaluate( |
| "/" + NODE_MANIFEST + |
| "/" + NODE_APPLICATION + |
| "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + |
| ":" + ATTRIBUTE_DEBUGGABLE, |
| new InputSource(manifestFile.getContents())); |
| |
| // default is not debuggable, which is the same behavior as parseBoolean |
| return Boolean.parseBoolean(value); |
| } |
| |
| /** |
| * Returns the value of the versionCode attribute or -1 if the value is not set. |
| * @param manifestFile the manifest file to read the attribute from. |
| * @return the integer value or -1 if not set. |
| * @throws XPathExpressionException |
| * @throws StreamException If any error happens when reading the manifest. |
| */ |
| public static int getVersionCode(IAbstractFile manifestFile) |
| throws XPathExpressionException, StreamException { |
| XPath xPath = AndroidXPathFactory.newXPath(); |
| |
| String result = xPath.evaluate( |
| "/" + NODE_MANIFEST + |
| "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + |
| ":" + ATTRIBUTE_VERSIONCODE, |
| new InputSource(manifestFile.getContents())); |
| |
| try { |
| return Integer.parseInt(result); |
| } catch (NumberFormatException e) { |
| return -1; |
| } |
| } |
| |
| /** |
| * Returns whether the version Code attribute is set in a given manifest. |
| * @param manifestFile the manifest to check |
| * @return true if the versionCode attribute is present and its value is not empty. |
| * @throws XPathExpressionException |
| * @throws StreamException If any error happens when reading the manifest. |
| */ |
| public static boolean hasVersionCode(IAbstractFile manifestFile) |
| throws XPathExpressionException, StreamException { |
| XPath xPath = AndroidXPathFactory.newXPath(); |
| |
| Object result = xPath.evaluate( |
| "/" + NODE_MANIFEST + |
| "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + |
| ":" + ATTRIBUTE_VERSIONCODE, |
| new InputSource(manifestFile.getContents()), |
| XPathConstants.NODE); |
| |
| if (result != null) { |
| Node node = (Node)result; |
| if (node.getNodeValue().length() > 0) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Returns the value of the minSdkVersion attribute. |
| * <p/> |
| * If the attribute is set with an int value, the method returns an Integer object. |
| * <p/> |
| * If the attribute is set with a codename, it returns the codename as a String object. |
| * <p/> |
| * If the attribute is not set, it returns null. |
| * |
| * @param manifestFile the manifest file to read the attribute from. |
| * @return the attribute value. |
| * @throws XPathExpressionException |
| * @throws StreamException If any error happens when reading the manifest. |
| */ |
| public static Object getMinSdkVersion(IAbstractFile manifestFile) |
| throws XPathExpressionException, StreamException { |
| XPath xPath = AndroidXPathFactory.newXPath(); |
| |
| String result = xPath.evaluate( |
| "/" + NODE_MANIFEST + |
| "/" + NODE_USES_SDK + |
| "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + |
| ":" + ATTRIBUTE_MIN_SDK_VERSION, |
| new InputSource(manifestFile.getContents())); |
| |
| try { |
| return Integer.valueOf(result); |
| } catch (NumberFormatException e) { |
| return result.length() > 0 ? result : null; |
| } |
| } |
| |
| /** |
| * Returns the value of the targetSdkVersion attribute (defaults to 1 if the attribute is |
| * not set), or -1 if the value is a codename. |
| * @param manifestFile the manifest file to read the attribute from. |
| * @return the integer value or -1 if not set. |
| * @throws XPathExpressionException |
| * @throws StreamException If any error happens when reading the manifest. |
| */ |
| public static Integer getTargetSdkVersion(IAbstractFile manifestFile) |
| throws XPathExpressionException, StreamException { |
| XPath xPath = AndroidXPathFactory.newXPath(); |
| |
| String result = xPath.evaluate( |
| "/" + NODE_MANIFEST + |
| "/" + NODE_USES_SDK + |
| "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + |
| ":" + ATTRIBUTE_TARGET_SDK_VERSION, |
| new InputSource(manifestFile.getContents())); |
| |
| try { |
| return Integer.valueOf(result); |
| } catch (NumberFormatException e) { |
| return result.length() > 0 ? -1 : null; |
| } |
| } |
| |
| /** |
| * Returns the application icon for a given manifest. |
| * @param manifestFile the manifest to parse. |
| * @return the icon or null (or empty) if not found. |
| * @throws XPathExpressionException |
| * @throws StreamException If any error happens when reading the manifest. |
| */ |
| public static String getApplicationIcon(IAbstractFile manifestFile) |
| throws XPathExpressionException, StreamException { |
| XPath xPath = AndroidXPathFactory.newXPath(); |
| |
| return xPath.evaluate( |
| "/" + NODE_MANIFEST + |
| "/" + NODE_APPLICATION + |
| "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + |
| ":" + ATTRIBUTE_ICON, |
| new InputSource(manifestFile.getContents())); |
| } |
| |
| /** |
| * Returns the application label for a given manifest. |
| * @param manifestFile the manifest to parse. |
| * @return the label or null (or empty) if not found. |
| * @throws XPathExpressionException |
| * @throws StreamException If any error happens when reading the manifest. |
| */ |
| public static String getApplicationLabel(IAbstractFile manifestFile) |
| throws XPathExpressionException, StreamException { |
| XPath xPath = AndroidXPathFactory.newXPath(); |
| |
| return xPath.evaluate( |
| "/" + NODE_MANIFEST + |
| "/" + NODE_APPLICATION + |
| "/@" + AndroidXPathFactory.DEFAULT_NS_PREFIX + |
| ":" + ATTRIBUTE_LABEL, |
| new InputSource(manifestFile.getContents())); |
| } |
| |
| /** |
| * Combines a java package, with a class value from the manifest to make a fully qualified |
| * class name |
| * @param javaPackage the java package from the manifest. |
| * @param className the class name from the manifest. |
| * @return the fully qualified class name. |
| */ |
| public static String combinePackageAndClassName(String javaPackage, String className) { |
| if (className == null || className.length() == 0) { |
| return javaPackage; |
| } |
| if (javaPackage == null || javaPackage.length() == 0) { |
| return className; |
| } |
| |
| // the class name can be a subpackage (starts with a '.' |
| // char), a simple class name (no dot), or a full java package |
| boolean startWithDot = (className.charAt(0) == '.'); |
| boolean hasDot = (className.indexOf('.') != -1); |
| if (startWithDot || hasDot == false) { |
| |
| // add the concatenation of the package and class name |
| if (startWithDot) { |
| return javaPackage + className; |
| } else { |
| return javaPackage + '.' + className; |
| } |
| } else { |
| // just add the class as it should be a fully qualified java name. |
| return className; |
| } |
| } |
| |
| /** |
| * Given a fully qualified activity name (e.g. com.foo.test.MyClass) and given a project |
| * package base name (e.g. com.foo), returns the relative activity name that would be used |
| * the "name" attribute of an "activity" element. |
| * |
| * @param fullActivityName a fully qualified activity class name, e.g. "com.foo.test.MyClass" |
| * @param packageName The project base package name, e.g. "com.foo" |
| * @return The relative activity name if it can be computed or the original fullActivityName. |
| */ |
| public static String extractActivityName(String fullActivityName, String packageName) { |
| if (packageName != null && fullActivityName != null) { |
| if (packageName.length() > 0 && fullActivityName.startsWith(packageName)) { |
| String name = fullActivityName.substring(packageName.length()); |
| if (name.length() > 0 && name.charAt(0) == '.') { |
| return name; |
| } |
| } |
| } |
| |
| return fullActivityName; |
| } |
| } |