com.android.manifmerger.ManifestSystemProperty Maven / Gradle / Ivy
/*
* Copyright (C) 2016 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.manifmerger;
import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.ide.common.blame.SourceFilePosition;
import com.android.ide.common.blame.SourcePosition;
import com.android.utils.SdkUtils;
import com.android.utils.XmlUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* List of manifest files properties that can be directly overridden without using a
* placeholder.
*/
public enum ManifestSystemProperty implements ManifestMerger2.AutoAddingProperty {
/**
* Allow setting the merged manifest file package name.
*/
PACKAGE {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElement(this, actionRecorder, value, document.getRootNode());
}
},
/**
* @see
* http://developer.android.com/guide/topics/manifest/manifest-element.html#vcode
*/
VERSION_CODE {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value, document.getRootNode());
}
},
/**
* @see
* http://developer.android.com/guide/topics/manifest/manifest-element.html#vname
*/
VERSION_NAME {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value, document.getRootNode());
}
},
/**
* @see
* http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#min
*/
MIN_SDK_VERSION {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value,
createOrGetUseSdk(actionRecorder, document));
}
},
/**
* @see
* http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#target
*/
TARGET_SDK_VERSION {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value,
createOrGetUseSdk(actionRecorder, document));
}
},
/**
* @see
* http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#max
*/
MAX_SDK_VERSION {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value,
createOrGetUseSdk(actionRecorder, document));
}
},
/**
* Name of the instrumentation runner.
*
* @see
* http://developer.android.com/guide/topics/manifest/instrumentation-element.html
*/
NAME {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value,
createOrGetInstrumentation(actionRecorder, document));
}
},
/**
* Target package for the instrumentation.
*
* @see
* http://developer.android.com/guide/topics/manifest/instrumentation-element.html
*/
TARGET_PACKAGE {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value,
createOrGetInstrumentation(actionRecorder, document));
}
},
/**
* Functional test attribute for the instrumentation.
*
* @see
* http://developer.android.com/guide/topics/manifest/instrumentation-element.html
*/
FUNCTIONAL_TEST {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value,
createOrGetInstrumentation(actionRecorder, document));
}
},
/**
* Handle profiling attribute for the instrumentation.
*
* @see
* http://developer.android.com/guide/topics/manifest/instrumentation-element.html
*/
HANDLE_PROFILING {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value,
createOrGetInstrumentation(actionRecorder, document));
}
},
/**
* Label attribute for the instrumentation.
*
* @see
* http://developer.android.com/guide/topics/manifest/instrumentation-element.html
*/
LABEL {
@Override
public void addTo(@NonNull ActionRecorder actionRecorder,
@NonNull XmlDocument document,
@NonNull String value) {
addToElementInAndroidNS(this, actionRecorder, value,
createOrGetInstrumentation(actionRecorder, document));
}
};
public String toCamelCase() {
return SdkUtils.constantNameToCamelCase(name());
}
// utility method to add an attribute which name is derived from the enum name().
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 */));
}
// utility method to add an attribute in android namespace which local name is derived from
// the enum name().
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 */
)
);
}
// utility method to create or get an existing use-sdk xml element under manifest.
// this could be made more generic by adding more metadata to the enum but since there is
// only one case so far, keep it simple.
@NonNull
private static XmlElement createOrGetUseSdk(
@NonNull ActionRecorder actionRecorder, @NonNull XmlDocument document) {
return createOrGetElement(actionRecorder, document,
ManifestModel.NodeTypes.USES_SDK, "use-sdk injection requested");
}
/** See above for details, similar like for uses-sdk tag*/
@NonNull
private static XmlElement createOrGetInstrumentation(
@NonNull ActionRecorder actionRecorder, @NonNull XmlDocument document) {
return createOrGetElement(actionRecorder, document,
ManifestModel.NodeTypes.INSTRUMENTATION, "instrumentation injection requested");
}
@NonNull
private static XmlElement createOrGetElement(
@NonNull ActionRecorder actionRecorder, @NonNull XmlDocument document,
@NonNull ManifestModel.NodeTypes nodeType, @NonNull String message) {
Element manifest = document.getXml().getDocumentElement();
NodeList nodes = manifest.getElementsByTagName(nodeType.toXmlName());
if (nodes.getLength() == 0) {
nodes = manifest.getElementsByTagNameNS(
SdkConstants.ANDROID_URI, nodeType.toXmlName());
}
if (nodes.getLength() == 0) {
// create it first.
Element node = manifest.getOwnerDocument().createElement(nodeType.toXmlName());
manifest.appendChild(node);
XmlElement xmlElement = new XmlElement(node, document);
Actions.NodeRecord nodeRecord = new Actions.NodeRecord(
Actions.ActionType.INJECTED,
new SourceFilePosition(xmlElement.getSourceFile(), SourcePosition.UNKNOWN),
xmlElement.getId(),
message,
NodeOperationType.STRICT);
actionRecorder.recordNodeAction(xmlElement, nodeRecord);
return xmlElement;
} else {
return new XmlElement((Element) nodes.item(0), document);
}
}
private static String getAndroidPrefix(@NonNull Element xml) {
String toolsPrefix = XmlUtils.lookupNamespacePrefix(
xml, SdkConstants.ANDROID_URI, SdkConstants.ANDROID_NS_NAME, false);
if (!toolsPrefix.equals(SdkConstants.ANDROID_NS_NAME) && xml.getOwnerDocument()
.getDocumentElement().getAttribute("xmlns:" + toolsPrefix) == null) {
// this is weird, the document is using "android" prefix but it's not bound
// to our namespace. Add the proper xmlns declaration.
xml.setAttribute("xmlns:" + toolsPrefix, SdkConstants.ANDROID_URI);
}
return toolsPrefix;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy