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

com.tencent.tinker.build.gradle.task.TinkerManifestAction.groovy Maven / Gradle / Ivy

Go to download

Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstalling apk.

The newest version!
/*
 * Tencent is pleased to support the open source community by making Tinker available.
 *
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 *
 * https://opensource.org/licenses/BSD-3-Clause
 *
 * 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.tencent.tinker.build.gradle.task

import com.tencent.tinker.build.gradle.TinkerBuildPath
import com.tencent.tinker.build.util.FileOperation
import com.tencent.tinker.commons.util.IOHelper
import groovy.xml.Namespace
import org.gradle.api.Action
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.Task

/**
 * The configuration properties.
 *
 * @author zhangshaowen
 */
public class TinkerManifestAction implements Action {
    static final String TINKER_ID = "TINKER_ID"
    static final String TINKER_ID_PREFIX = "tinker_id_"

    private final Project project

    final Map outputNameToManifestMap = new HashMap<>()

    TinkerManifestAction(Project project) {
        this.project = project
    }

    @Override
    void execute(Task task) {
        updateManifest()
    }

    private void updateManifest() {
        // Parse the AndroidManifest.xml
        String tinkerValue = project.extensions.tinkerPatch.buildConfig.tinkerId
        boolean appendOutputNameToTinkerId = project.extensions.tinkerPatch.buildConfig.appendOutputNameToTinkerId

        if (tinkerValue == null || tinkerValue.isEmpty()) {
            throw new GradleException('tinkerId is not set!!!')
        }

        tinkerValue = TINKER_ID_PREFIX + tinkerValue

        def agpIntermediatesDir = new File(project.buildDir, 'intermediates')
        outputNameToManifestMap.each { String outputName, File manifest ->
            def manifestPath = manifest.getAbsolutePath()
            def finalTinkerValue = tinkerValue
            if (appendOutputNameToTinkerId && !outputName.isEmpty()) {
                finalTinkerValue += "_${outputName}"
            }

            project.logger.error("tinker add ${finalTinkerValue} to your AndroidManifest.xml ${manifestPath}")

            writeManifestMeta(manifestPath, TINKER_ID, finalTinkerValue)
            addApplicationToLoaderPattern(manifestPath)
            File manifestFile = new File(manifestPath)
            if (manifestFile.exists()) {
                def manifestRelPath = agpIntermediatesDir.toPath().relativize(manifestFile.toPath()).toString()
                def manifestDestPath = new File(project.file(TinkerBuildPath.getTinkerIntermediates(project)), manifestRelPath)
                FileOperation.copyFileUsingStream(manifestFile, manifestDestPath)
                project.logger.error("tinker gen AndroidManifest.xml in ${manifestDestPath}")
            }
        }
    }

    private static void writeManifestMeta(String manifestPath, String name, String value) {
        def ns = new Namespace("http://schemas.android.com/apk/res/android", "android")
        def isr = null
        def pw = null
        try {
            isr = new InputStreamReader(new FileInputStream(manifestPath), "utf-8")
            def xml = new XmlParser().parse(isr) as Node
            def application = xml.application[0] as Node
            if (application) {
                def metaDataTags = application['meta-data']

                // remove any old TINKER_ID elements
                def tinkerId = metaDataTags.findAll {
                    it.attributes()[ns.name].equals(name)
                }.each {
                    it.parent().remove(it)
                }

                // Add the new TINKER_ID element
                application.appendNode('meta-data', [(ns.prefix + ':name'): name, (ns.prefix + ':value'): value])

                // Write the manifest file
                pw = new PrintWriter(manifestPath, "utf-8")
                def printer = new XmlNodePrinter(pw)
                printer.preserveWhitespace = true
                printer.print(xml)
            }
        } finally {
            IOHelper.closeQuietly(pw)
            IOHelper.closeQuietly(isr)
        }
    }

    private void addApplicationToLoaderPattern(String manifestPath) {
        Iterable loader = project.extensions.tinkerPatch.dex.loader
        String applicationName = readManifestApplicationName(manifestPath)

        if (applicationName != null && !loader.contains(applicationName)) {
            loader.add(applicationName)
            project.logger.error("tinker add ${applicationName} to dex loader pattern")
        }
        String loaderClass = "com.tencent.tinker.loader.*"
        if (!loader.contains(loaderClass)) {
            loader.add(loaderClass)
            project.logger.error("tinker add ${loaderClass} to dex loader pattern")
        }
    }

    private static String readManifestApplicationName(String manifestPath) {
        def isr = null
        try {
            isr = new InputStreamReader(new FileInputStream(manifestPath), "utf-8")
            def xml = new XmlParser().parse(isr)
            def ns = new Namespace("http://schemas.android.com/apk/res/android", "android")

            def application = xml.application[0]
            if (application) {
                return application.attributes()[ns.name]
            } else {
                return null
            }
        } finally {
            IOHelper.closeQuietly(isr)
        }
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy