
org.droitateddb.manifest.AndroidManifestProcessor Maven / Gradle / Ivy
/*
* Copyright (C) 2014 The droitated DB Authors
*
* 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 org.droitateddb.manifest;
import org.droitateddb.processor.ContentProviderData;
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* Allows parsing and changing data of the AndroidManifest.xml
*
* @author Alexander Frank
* @author Falk Appel
*/
public class AndroidManifestProcessor {
private final File manifestFile;
private final String generatedPackage;
public AndroidManifestProcessor(final File manifestFile, final String generatedPackage) {
this.manifestFile = manifestFile;
this.generatedPackage = generatedPackage;
}
public AndroidManifest parse() throws Exception {
InputStream manifestStream = new FileInputStream(manifestFile);
try {
Document document = createDocument(manifestStream);
Element root = document.getDocumentElement();
String usedPackage = root.getAttribute("package");
return new AndroidManifest(usedPackage, findExistingContentProvider(root));
} finally {
manifestStream.close();
}
}
private List findExistingContentProvider(final Element root) {
NodeList nodesUnderApplication = getApplicationNodeFromRoot(root).getChildNodes();
List data = new ArrayList(nodesUnderApplication.getLength());
for (int i = 0; i < nodesUnderApplication.getLength(); i++) {
Node node = nodesUnderApplication.item(i);
if (node.getNodeName().equals("provider")) {
NamedNodeMap attributes = node.getAttributes();
String providerName = attributes.getNamedItem("android:name").getTextContent();
String authority = attributes.getNamedItem("android:authorities").getTextContent();
Node exportedAttribute = attributes.getNamedItem("android:exported");
boolean exported = false;
if (exportedAttribute != null) {
exported = Boolean.parseBoolean(exportedAttribute.getTextContent());
}
data.add(new ContentProviderData(providerName, authority, exported));
}
}
return data;
}
private static Document createDocument(final InputStream manifestStream) throws Exception {
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
return documentBuilder.parse(manifestStream);
}
private static Node getApplicationNodeFromRoot(final Element root) {
NodeList applicationTag = root.getElementsByTagName("application");
if (applicationTag.getLength() != 1) {
throw new IllegalStateException(
"Wrong number of application tags found within AndroidManifest.xml. There are " + applicationTag.getLength() + " tags");
}
return applicationTag.item(0);
}
public ManifestBuilder change() throws Exception {
return new ManifestBuilder(manifestFile, parse(),generatedPackage);
}
public static final class ManifestBuilder {
private final File manifestFile;
private final LinkedList addedProviders;
private final AndroidManifest androidManifest;
private final String generatedPackage;
private ManifestBuilder(final File manifestFile, final AndroidManifest androidManifest, final String generatedPackage) {
this.manifestFile = manifestFile;
this.androidManifest = androidManifest;
this.generatedPackage = generatedPackage;
addedProviders = new LinkedList();
}
public ManifestBuilder addProviderIfNotExists(final ContentProviderData contentProviderData) {
addedProviders.add(contentProviderData);
return this;
}
public void commit() throws Exception {
Document document = createDocument(new FileInputStream(manifestFile));
Element root = document.getDocumentElement();
Node application = getApplicationNodeFromRoot(root);
boolean addedSomething = addProviderTagsIfNeeded(document, application);
boolean removedSomething = removeUnusedProviders(application);
saveToAndroidManifest(document, addedSomething || removedSomething);
}
private boolean addProviderTagsIfNeeded(final Document document, final Node application) {
boolean changesSomething = false;
for (ContentProviderData added : addedProviders) {
if (notContainedInProviderList(added, androidManifest.getContentProviders())) {
Element newProvider = document.createElement("provider");
newProvider.setAttribute("android:name", added.getCanonicalName());
newProvider.setAttribute("android:authorities", added.getAuthority());
newProvider.setAttribute("android:exported", Boolean.toString(added.isExported()));
newProvider.setAttribute("droitateddb:generated", "true");
application.appendChild(newProvider);
document.getDocumentElement().setAttribute("xmlns:droitateddb", "http://droitateddb.org");
changesSomething = true;
}
}
return changesSomething;
}
private boolean removeUnusedProviders(final Node application) {
boolean changedSomething = false;
for (ContentProviderData inManifest : androidManifest.getContentProviders()) {
if (notContainedInProviderList(inManifest, addedProviders)) {
changedSomething |= removeSingleProvider(inManifest, application);
}
}
return changedSomething;
}
private boolean removeSingleProvider(final ContentProviderData inManifest, final Node application) {
NodeList applicationChildren = application.getChildNodes();
for (int i = 0; i < applicationChildren.getLength(); i++) {
Node child = applicationChildren.item(i);
if (child.getNodeName().equals("provider") && isGenerated(child)) {
NamedNodeMap attributes = child.getAttributes();
Node name = attributes.getNamedItem("android:name");
Node authority = attributes.getNamedItem("android:authorities");
if (authority.getTextContent().equals(inManifest.getAuthority()) && namesAreEqual(name.getTextContent(), inManifest.getCanonicalName())) {
application.removeChild(child);
return true;
}
}
}
return false;
}
private boolean notContainedInProviderList(final ContentProviderData added, final List providers) {
for (ContentProviderData inManifest : providers) {
if (inManifest.getAuthority().equals(added.getAuthority()) && namesAreEqual(inManifest.getCanonicalName(), added.getCanonicalName()) &&
inManifest.isExported() == added.isExported()) {
return false;
}
}
return true;
}
private boolean namesAreEqual(final String providerName, final String otherProviderName) {
String comparableManifestName = packagedProviderName(providerName);
String comparableProviderName = packagedProviderName(otherProviderName);
return comparableManifestName.equals(comparableProviderName);
}
private String packagedProviderName(final String name) {
String packagedName;
if (name.startsWith(".")) {
packagedName = generatedPackage + name;
} else {
packagedName = name;
}
return packagedName;
}
private boolean isGenerated(final Node child) {
NamedNodeMap attributes = child.getAttributes();
Node generated = attributes.getNamedItem("droitateddb:generated");
if (generated == null) {
return false;
}
return Boolean.parseBoolean(generated.getTextContent());
}
private void saveToAndroidManifest(final Document document, final boolean changedSomething) throws TransformerFactoryConfigurationError,
TransformerConfigurationException,
FileNotFoundException, TransformerException {
if (changedSomething) {
DOMSource source = new DOMSource(document);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
StreamResult result = new StreamResult(new FileOutputStream(manifestFile));
transformer.transform(source, result);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy