@SuppressWarnings("SpellCheckingInspection") private ImmutableList<XmlElement> initMergeableChildren() { ImmutableList.Builder<XmlElement> mergeableNodes = new ImmutableList.Builder<XmlElement>(); NodeList nodeList = getXml().getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node instanceof Element) { XmlElement xmlElement = new XmlElement((Element) node, mDocument); mergeableNodes.add(xmlElement); } } return mergeableNodes.build(); }
/** * Reorder application element * * @param xmlElement the root element of the manifest document. */ private static void reOrderApplication(@NonNull XmlElement xmlElement) { // look up application element. Optional<XmlElement> element = xmlElement .getNodeByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null); if (!element.isPresent()) { return; } XmlElement applicationElement = element.get(); List<Node> comments = XmlElement.getLeadingComments(applicationElement.getXml()); // move the application's comments if any. for (Node comment : comments) { xmlElement.getXml().removeChild(comment); xmlElement.getXml().appendChild(comment); } // remove the application element and add it back, it will be automatically placed last. xmlElement.getXml().removeChild(applicationElement.getXml()); xmlElement.getXml().appendChild(applicationElement.getXml()); }
/** * Reorder uses-sdk element * * @param xmlElement the root element of the manifest document. */ private static void reOrderUsesSdk(@NonNull XmlElement xmlElement) { // look up application element. Optional<XmlElement> element = xmlElement .getNodeByTypeAndKey(ManifestModel.NodeTypes.USES_SDK, null); if (!element.isPresent()) { return; } XmlElement usesSdk = element.get(); Node firstChild = xmlElement.getXml().getFirstChild(); // already the first element ? if (firstChild == usesSdk.getXml()) { return; } List<Node> comments = XmlElement.getLeadingComments(usesSdk.getXml()); // move the application's comments if any. for (Node comment : comments) { xmlElement.getXml().removeChild(comment); xmlElement.getXml().insertBefore(comment, firstChild); } // remove the application element and add it back, it will be automatically placed last. xmlElement.getXml().removeChild(usesSdk.getXml()); xmlElement.getXml().insertBefore(usesSdk.getXml(), firstChild); }
/** * Removes the android namespace from all nodes. */ public void clearNodeNamespaces() { clearNodeNamespaces(getRootNode().getXml()); }
private static void addService(XmlDocument document, XmlElement application ) { // <service // android:name="com.android.tools.fd.runtime.InstantRunService" // android:exported="true"/> Element service = document.getXml().createElement(SdkConstants.TAG_SERVICE); setAndroidAttribute(service, SdkConstants.ATTR_NAME, BOOTSTRAP_INSTANT_RUN_SERVICE); // Export it so we can start it with a shell command from adb. setAndroidAttribute(service, SdkConstants.ATTR_EXPORTED, SdkConstants.VALUE_TRUE); application.getXml().appendChild(service); }
/** * Add an element and its leading comments as the last sub-element of the current element. * @param elementToBeAdded xml element to be added to the current element. * @param mergingReport the merging report to log errors and actions. */ private void addElement( @NonNull XmlElement elementToBeAdded, @NonNull MergingReport.Builder mergingReport) { List<Node> comments = getLeadingComments(elementToBeAdded.getXml()); // record all the actions before the node is moved from the library document to the main // merged document. mergingReport.getActionRecorder().recordDefaultNodeAction(elementToBeAdded); // only in the new file, just import it. Node node = getXml().getOwnerDocument().adoptNode(elementToBeAdded.getXml()); getXml().appendChild(node); // also adopt the child's comments if any. for (Node comment : comments) { Node newComment = getXml().getOwnerDocument().adoptNode(comment); getXml().insertBefore(newComment, node); } mergingReport.getLogger().verbose("Adopted " + node); }
@NonNull private static XmlDocument instantRunReplacement(XmlDocument document) { Optional<XmlElement> applicationOptional = document .getByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null /* keyValue */); if (applicationOptional.isPresent()) { XmlElement application = applicationOptional.get(); setAttributeToTrue(application.getXml(), SdkConstants.ATTR_ENABLED); setAttributeToTrue(application.getXml(), SdkConstants.ATTR_HAS_CODE); addService(document, application); } else { throw new RuntimeException("Application not defined in AndroidManifest.xml"); } return document.reparse(); }
/** * Validate an xml declaration with 'tools:node="removeAll" annotation. There should not * be any other attribute declaration on this element. */ private static void validateRemoveAllOperation(@NonNull MergingReport.Builder mergingReport, @NonNull XmlElement element) { NamedNodeMap attributes = element.getXml().getAttributes(); if (attributes.getLength() > 1) { List<String> extraAttributeNames = new ArrayList<String>(); for (int i = 0; i < attributes.getLength(); i++) { Node item = attributes.item(i); if (!(SdkConstants.TOOLS_URI.equals(item.getNamespaceURI()) && NodeOperationType.NODE_LOCAL_NAME.equals(item.getLocalName()))) { extraAttributeNames.add(item.getNodeName()); } } String message = String.format( "Element %1$s at %2$s annotated with 'tools:node=\"removeAll\"' cannot " + "have other attributes : %3$s", element.getId(), element.printPosition(), Joiner.on(',').join(extraAttributeNames) ); element.addMessage(mergingReport, ERROR, message); } }
private static void validateManifestAttribute( @NonNull MergingReport.Builder mergingReport, @NonNull XmlElement manifest, XmlDocument.Type fileType) { Attr attributeNode = manifest.getXml().getAttributeNode(AndroidManifest.ATTRIBUTE_PACKAGE); // it's ok for an overlay to not have a package name, it's not ok for a main manifest // and it's a warning for a library. if (attributeNode == null && fileType != XmlDocument.Type.OVERLAY) { manifest.addMessage(mergingReport, fileType == XmlDocument.Type.MAIN ? ERROR : WARNING, String.format( "Missing 'package' declaration in manifest at %1$s", manifest.printPosition())); } }
/** * Handles presence of custom elements (elements not part of the android or tools * namespaces). Such elements are merged unchanged into the resulting document, and * optionally, the namespace definition is added to the merged document root element. * @param customElement the custom element present in the lower priority document. * @param mergingReport the merging report to log errors and actions. */ private void handleCustomElement(@NonNull XmlElement customElement, @NonNull MergingReport.Builder mergingReport) { addElement(customElement, mergingReport); // add the custom namespace to the document generation. String nodeName = customElement.getXml().getNodeName(); if (!nodeName.contains(":")) { return; } @NonNull String prefix = nodeName.substring(0, nodeName.indexOf(':')); String namespace = customElement.getDocument().getRootNode() .getXml().getAttribute(SdkConstants.XMLNS_PREFIX + prefix); if (namespace != null) { getDocument().getRootNode().getXml().setAttributeNS( SdkConstants.XMLNS_URI, SdkConstants.XMLNS_PREFIX + prefix, namespace); } }
/** * Set android:testOnly="true" to ensure APK will be rejected by the Play store. */ @NonNull private static XmlDocument addTestOnlyAttribute(XmlDocument document) { Optional<XmlElement> applicationOptional = document .getByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null /* keyValue */); if (applicationOptional.isPresent()) { XmlElement application = applicationOptional.get(); setAndroidAttribute(application.getXml(), SdkConstants.ATTR_TEST_ONLY, SdkConstants.VALUE_TRUE); } return document.reparse(); }
private static Integer getGlEsVersion(@NonNull XmlElement xmlElement) { Attr glEsVersion = xmlElement.getXml() .getAttributeNodeNS(ANDROID_URI, AndroidManifest.ATTRIBUTE_GLESVERSION); if (glEsVersion == null) { return null; } return getHexValue(glEsVersion); }
/** * Enforces {@link com.android.SdkConstants#ANDROID_URI} declaration in the top level element. * It is possible that the original manifest file did not contain any attribute declaration, * therefore not requiring a xmlns: declaration. Yet the implicit elements handling may have * added attributes requiring the namespace declaration. */ private static void enforceAndroidNamespaceDeclaration(@NonNull XmlDocument xmlDocument) { XmlElement manifest = xmlDocument.getRootNode(); for (XmlAttribute xmlAttribute : manifest.getAttributes()) { if (xmlAttribute.getXml().getName().startsWith(SdkConstants.XMLNS) && SdkConstants.ANDROID_URI.equals(xmlAttribute.getValue())) { return; } } // if we are here, we did not find the namespace declaration, add it. manifest.getXml().setAttribute(SdkConstants.XMLNS + ":" + "android", SdkConstants.ANDROID_URI); }
private static void checkSelectorPresence(@NonNull MergingReport.Builder mergingReport, @NonNull XmlElement element) { Attr selectorAttribute = element.getXml().getAttributeNodeNS(SdkConstants.TOOLS_URI, Selector.SELECTOR_LOCAL_NAME); if (selectorAttribute!=null && !element.supportsSelector()) { String message = String.format( "Unsupported tools:selector=\"%1$s\" found on node %2$s at %3$s", selectorAttribute.getValue(), element.getId(), element.printPosition()); element.addMessage(mergingReport, ERROR, message); } }
private static void addToElement( @NonNull ManifestSystemProperty manifestSystemProperty, @NonNull ActionRecorder actionRecorder, String value, @NonNull XmlElement to) { to.getXml().setAttribute(manifestSystemProperty.toCamelCase(), value); XmlAttribute xmlAttribute = new XmlAttribute(to, to.getXml().getAttributeNode(manifestSystemProperty.toCamelCase()), null); actionRecorder.recordAttributeAction(xmlAttribute, new Actions.AttributeRecord( Actions.ActionType.INJECTED, new SourceFilePosition(to.getSourceFile(), SourcePosition.UNKNOWN), xmlAttribute.getId(), null, /* reason */ null /* attributeOperationType */)); }
/** * Cleans all attributes belonging to the {@link com.android.SdkConstants#TOOLS_URI} namespace. * * @param document the xml document to clean * @param logger logger to use in case of errors and warnings. * @return the cleaned document or null if an error occurred. */ @Nullable public static XmlDocument cleanToolsReferences( @NonNull ManifestMerger2.MergeType mergeType, @NonNull XmlDocument document, @NonNull ILogger logger) { Preconditions.checkNotNull(document); Preconditions.checkNotNull(logger); MergingReport.Result result = cleanToolsReferences( mergeType, document.getRootNode().getXml(), logger); return result == MergingReport.Result.SUCCESS ? document.reparse() : null; }
private static void addToElementInAndroidNS( @NonNull ManifestSystemProperty manifestSystemProperty, @NonNull ActionRecorder actionRecorder, String value, @NonNull XmlElement to) { String toolsPrefix = getAndroidPrefix(to.getXml()); to.getXml().setAttributeNS(SdkConstants.ANDROID_URI, toolsPrefix + XmlUtils.NS_SEPARATOR + manifestSystemProperty.toCamelCase(), value); Attr attr = to.getXml().getAttributeNodeNS(SdkConstants.ANDROID_URI, manifestSystemProperty.toCamelCase()); XmlAttribute xmlAttribute = new XmlAttribute(to, attr, null); actionRecorder.recordAttributeAction(xmlAttribute, new Actions.AttributeRecord( Actions.ActionType.INJECTED, new SourceFilePosition(to.getSourceFile(), SourcePosition.UNKNOWN), xmlAttribute.getId(), null, /* reason */ null /* attributeOperationType */ ) ); }
actionRecorder.recordImpliedNodeAction(xmlElement, reason); getRootNode().getXml().appendChild(elementNS); return Optional.of(elementNS);
/** * Returns a pretty string representation of this document. */ @NonNull public String prettyPrint() { return XmlPrettyPrinter.prettyPrint( getXml(), XmlFormatPreferences.defaults(), XmlFormatStyle.get(getRootNode().getXml()), null, /* endOfLineSeparator */ false /* endWithNewLine */); }
getName().addToNode(higherPriorityElement.getXml(), mergedValue);