All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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;

    public AndroidManifestProcessor(final File manifestFile) {
        this.manifestFile = manifestFile;
    }

    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());
    }

    public static final class ManifestBuilder {

        private final File manifestFile;
        private final LinkedList addedProviders;
        private final AndroidManifest androidManifest;

        private ManifestBuilder(final File manifestFile, final AndroidManifest androidManifest) {
            this.manifestFile = manifestFile;
            this.androidManifest = androidManifest;
            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 = androidManifest.getPackageName() + 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