diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/RebuildManifestTask.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/RebuildManifestTask.kt new file mode 100644 index 000000000..f8fbd9782 --- /dev/null +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/RebuildManifestTask.kt @@ -0,0 +1,77 @@ +/* + * Tencent is pleased to support the open source community by making Tencent Shadow available. + * Copyright (C) 2019 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.shadow.core.gradle + +import groovy.xml.DOMBuilder +import org.w3c.dom.Document +import org.w3c.dom.Element +import java.io.File +import javax.xml.transform.OutputKeys +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult + +/** + * 解析插件的AndroidManifest.xml文件 + * 由于系统开放接口不提供广播的action信息,此处采用手动解析的方式处理,减少插件化的适配工作 + * 后续对于AndroidManifest.xml的处理可在此基础上扩展 + * + * @author xuedizi2009@163.com + */ +internal fun rebuildManifest(manifestFile: File) { + println("RebuildManifest task run") + val document = DOMBuilder.parse(manifestFile.reader(Charsets.UTF_8)) + try { + buildReceiver(document) + }catch (e : Exception){ + println("RebuildManifest fail , check tag") + } + writeDocument(document,manifestFile) +} + +fun writeDocument(document: Document,manifestFile : File) { + val newTransformer = TransformerFactory.newInstance().newTransformer() + newTransformer.setOutputProperty(OutputKeys.ENCODING,"UTF-8") + newTransformer.setOutputProperty(OutputKeys.INDENT,"yes") + newTransformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,"no") + newTransformer.transform(DOMSource(document),StreamResult(manifestFile.writer())) +} + +@Throws(Exception::class) +fun buildReceiver(document: Document){ + val receiverElements = document.getElementsByTagName("receiver") + for (receiverIndex in 0 until receiverElements.length){ + val receiverNode = receiverElements.item(receiverIndex) + val element = receiverNode as Element + val receiveNodeValue = element.getAttribute("android:name") + for (intentFilterIndex in 0 until receiverNode.childNodes.length){ + val intentFilterNode = receiverNode.childNodes.item(intentFilterIndex) + if(intentFilterNode.nodeName == "intent-filter"){ + for (actionIndex in 0 until intentFilterNode.childNodes.length){ + val actionNode = intentFilterNode.childNodes.item(actionIndex) + if(actionNode.nodeName == "action"){ + val metaDateElement = document.createElement("meta-data") + metaDateElement.setAttribute("android:name",(actionNode as Element).getAttribute("android:name")) + metaDateElement.setAttribute("android:value",receiveNodeValue) + receiverNode.appendChild(metaDateElement) + } + } + } + } + } +} diff --git a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt index 665fb4ba4..dde21f3b9 100644 --- a/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt +++ b/projects/sdk/core/gradle-plugin/src/main/kotlin/com/tencent/shadow/core/gradle/ShadowPlugin.kt @@ -18,8 +18,10 @@ package com.tencent.shadow.core.gradle +import com.android.build.gradle.AppExtension import com.android.build.gradle.AppPlugin import com.android.build.gradle.BaseExtension +import com.android.build.gradle.tasks.ProcessMultiApkApplicationManifest import com.tencent.shadow.core.gradle.extensions.PackagePluginExtension import com.tencent.shadow.core.transform.ShadowTransform import com.tencent.shadow.core.transform_kit.AndroidClassPoolBuilder @@ -61,6 +63,24 @@ class ShadowPlugin : Plugin { initAndroidClassPoolBuilder(baseExtension, project) createPackagePluginTasks(project) + + parseManifest(project) + } + } + + private fun parseManifest(project: Project){ + val appExtension: AppExtension = project.extensions.getByType(AppExtension::class.java) + appExtension.applicationVariants.all { variant -> + variant.outputs.all { output -> + val processManifestTask = output.processManifestProvider.get() + processManifestTask.doLast { + val manifestFile = File( + (processManifestTask as ProcessMultiApkApplicationManifest).multiApkManifestOutputDirectory.get().asFile, + "AndroidManifest.xml" + ) + rebuildManifest(manifestFile) + } + } } } diff --git a/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/LoadPluginBloc.kt b/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/LoadPluginBloc.kt index 183e0bf4a..121274a72 100644 --- a/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/LoadPluginBloc.kt +++ b/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/LoadPluginBloc.kt @@ -96,14 +96,9 @@ object LoadPluginBloc { packageArchiveInfo }) - val buildManifestInfo = executorService.submit(Callable { - ParseManifestBloc.parse(hostAppContext, installedApk) - }) - val buildPluginInfo = executorService.submit(Callable { val packageInfo = getPackageInfo.get() - val manifestInfo = buildManifestInfo.get() - ParsePluginApkBloc.parse(packageInfo, manifestInfo, loadParameters, hostAppContext) + ParsePluginApkBloc.parse(packageInfo, loadParameters, hostAppContext) }) val buildPackageManager = executorService.submit(Callable { diff --git a/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/ParseManifestBloc.kt b/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/ParseManifestBloc.kt deleted file mode 100644 index b296f23f8..000000000 --- a/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/ParseManifestBloc.kt +++ /dev/null @@ -1,90 +0,0 @@ -package com.tencent.shadow.core.loader.blocs - -import android.content.Context -import android.content.IntentFilter -import android.content.res.Resources -import com.tencent.shadow.core.common.InstalledApk -import com.tencent.shadow.core.loader.infos.ManifestInfo -import com.tencent.shadow.core.loader.infos.ManifestInfo.Receiver -import com.tencent.shadow.core.loader.infos.ManifestInfo.ReceiverIntentInfo -import org.xmlpull.v1.XmlPullParser - -/** - * 解析插件的AndroidManifest.xml文件 - * 由于系统开放接口不提供广播的action信息,此处采用手动解析的方式处理,减少插件化的适配工作 - * 后续对于AndroidManifest.xml的处理可在此基础上扩展 - * - * @author xuedizi2009@163.com - */ -object ParseManifestBloc { - - private const val ANDROID_RESOURCES = "http://schemas.android.com/apk/res/android" - private const val TAG_RECEIVER = "receiver" - private const val TAG_INTENT_FILTER = "intent-filter" - private const val TAG_ACTION = "action" - private const val ATTR_NAME = "name" - - @Throws(Exception::class) - fun parse(context: Context, installedApk: InstalledApk): ManifestInfo = ManifestInfo().apply { - val resources: Resources = newResource(context, installedApk) - val parser = resources.assets.openXmlResourceParser("AndroidManifest.xml") - val outerDepth = parser.depth - var type: Int - while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.depth > outerDepth) - ) { - if (type == XmlPullParser.START_TAG) { - parseBroadcastReceiver(parser, this) - } - } - } - - /** - * 此处如果使用[LoadPluginBloc.loadPlugin]构建的resources,将包含WebView的resources,故需要重新创建 - */ - private fun newResource(context: Context, installedApk: InstalledApk): Resources { - val packageArchiveInfo = - context.packageManager.getPackageArchiveInfo(installedApk.apkFilePath, 0)!! - val packageManager = context.packageManager - packageArchiveInfo.applicationInfo?.publicSourceDir = installedApk.apkFilePath - packageArchiveInfo.applicationInfo?.sourceDir = installedApk.apkFilePath - return packageManager.getResourcesForApplication(packageArchiveInfo.applicationInfo) - } - - private fun parseBroadcastReceiver(parser: XmlPullParser, manifestInfo: ManifestInfo) { - if (TAG_RECEIVER == parser.name) { - val receiver = Receiver(parser.getAttributeValue(ANDROID_RESOURCES, ATTR_NAME)) - val outerDepth = parser.depth - var type: Int - while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.depth > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue - } - if (TAG_INTENT_FILTER == parser.name) { - val receiverInfo = ReceiverIntentInfo() - parserIntent(parser, receiverInfo) - receiver.intents.add(receiverInfo) - } - } - manifestInfo.receivers.add(receiver) - } - } - - private fun parserIntent(parser: XmlPullParser, intentFilter: IntentFilter) { - val outerDepth = parser.depth - var type: Int - while (parser.next().also { type = it } != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.depth > outerDepth) - ) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue - } - if (TAG_ACTION == parser.name) { - val value = parser.getAttributeValue(ANDROID_RESOURCES, ATTR_NAME) - intentFilter.addAction(value) - } - } - } -} \ No newline at end of file diff --git a/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/ParsePluginApkBloc.kt b/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/ParsePluginApkBloc.kt index e51baf19f..0f8492585 100644 --- a/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/ParsePluginApkBloc.kt +++ b/projects/sdk/core/loader/src/main/kotlin/com/tencent/shadow/core/loader/blocs/ParsePluginApkBloc.kt @@ -19,6 +19,7 @@ package com.tencent.shadow.core.loader.blocs import android.content.Context +import android.content.pm.ActivityInfo import android.content.pm.PackageInfo import android.os.Build import com.tencent.shadow.core.load_parameters.LoadParameters @@ -41,7 +42,6 @@ object ParsePluginApkBloc { @Throws(ParsePluginApkException::class) fun parse( packageArchiveInfo: PackageInfo, - manifestInfo: ManifestInfo, loadParameters: LoadParameters, hostAppContext: Context ): PluginInfo { @@ -84,13 +84,12 @@ object ParsePluginApkBloc { pluginInfo.putActivityInfo(PluginActivityInfo(it.name, it.themeResource, it)) } - val receiveMap = manifestInfo.receivers.map { it.name to it }.toMap() packageArchiveInfo.receivers?.forEach { pluginInfo.putReceiverInfo( PluginReceiverInfo( it.name, it, - receiveMap[it.name]?.actions() + it.toReceiveActions() ) ) } @@ -100,4 +99,10 @@ object ParsePluginApkBloc { } return pluginInfo } + + private fun ActivityInfo.toReceiveActions(): List? { + return this.metaData?.keySet()?.filter { + this.metaData?.getString(it) == this.name + } + } }