com.tencent.tinker.build.gradle.task.TinkerManifestAction.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tinker-patch-gradle-plugin Show documentation
Show all versions of tinker-patch-gradle-plugin Show documentation
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)
}
}
}