package com.tws.plugin.core;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import tws.component.log.TwsLog;
import android.app.Application;
import android.content.pm.ActivityInfo;
import android.text.TextUtils;
import com.tws.plugin.content.DisplayConfig;
import com.tws.plugin.content.PluginActivityInfo;
import com.tws.plugin.content.PluginDescriptor;
import com.tws.plugin.content.PluginIntentFilter;
import com.tws.plugin.content.PluginProviderInfo;
import com.tws.plugin.util.ManifestReader;
import com.tws.plugin.util.ProcessUtil;
public class PluginManifestParser {
private static final String TAG = "rick_Print:PluginManifestParser";
public static PluginDescriptor parseManifest(String pluginPath) {
try {
ZipFile zipFile = new ZipFile(new File(pluginPath), ZipFile.OPEN_READ);
ZipEntry manifestXmlEntry = zipFile.getEntry(ManifestReader.DEFAULT_XML);
String manifestXml = ManifestReader.getManifestXMLFromAPK(zipFile, manifestXmlEntry);
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = factory.newPullParser();
parser.setInput(new StringReader(manifestXml));
int eventType = parser.getEventType();
String namespaceAndroid = null;
String packageName = null;
ArrayList<String> dependencies = null;
String pluginProcessName = null;
PluginDescriptor desciptor = new PluginDescriptor();
do {
switch (eventType) {
case XmlPullParser.START_DOCUMENT: {
break;
}
case XmlPullParser.START_TAG: {
String tag = parser.getName();
if ("manifest".equals(tag)) {
namespaceAndroid = parser.getNamespace("android");
packageName = parser.getAttributeValue(null, "package");
String versionCode = parser.getAttributeValue(namespaceAndroid, "versionCode");
String versionName = parser.getAttributeValue(namespaceAndroid, "versionName");
String platformBuildVersionCode = parser.getAttributeValue(namespaceAndroid,
"platformBuildVersionCode");
String platformBuildVersionName = parser.getAttributeValue(namespaceAndroid,
"platformBuildVersionName");
// 用这个字段来标记apk是独立apk,还是需要依赖主程序的class和resource
// 当这个值等于宿主程序packageName时,则认为这个插件是需要依赖宿主的class和resource的
String sharedUserId = parser.getAttributeValue(namespaceAndroid, "sharedUserId");
desciptor.setPackageName(packageName);
if (TextUtils.isEmpty(versionCode)) {
versionCode = "1";
}
if (TextUtils.isEmpty(versionName)) {
versionName = "1.0";
}
// 注意这里必须是code在前,方便后面获取code码操作
desciptor.setVersion(versionCode + DisplayConfig.SEPARATOR_VER + versionName);
desciptor.setPlatformBuildVersionCode(platformBuildVersionCode);
desciptor.setPlatformBuildVersionName(platformBuildVersionName);
desciptor.setStandalone(sharedUserId == null
|| !PluginLoader.getApplication().getPackageName().equals(sharedUserId));
TwsLog.d(TAG, "packageName=" + packageName + " versionCode=" + versionCode + " versionName="
+ versionName + " sharedUserId=" + sharedUserId);
} else if ("plugin-display".equals(tag)) {
// 解析插件的配置显示形态
String value = parser.getAttributeValue(namespaceAndroid, "value");
ArrayList<DisplayConfig> displayConfigs = desciptor.getDisplayConfigs();
if (null == displayConfigs) {
displayConfigs = new ArrayList<DisplayConfig>();
desciptor.setDisplayConfigs(displayConfigs);
}
final String[] displays = value.split(DisplayConfig.SEPARATOR_CONFIG);
for (int index = 0; index < displays.length; index++) {
DisplayConfig displayConfig = new DisplayConfig();
final String[] configs = displays[index].split(DisplayConfig.SEPARATOR_ATTRIBUTE);
for (int i = 0; i < configs.length; i++) {
final String[] values = configs[i].split(DisplayConfig.SEPARATOR_VALUATION);
if (values.length != 2) {
TwsLog.e(TAG, "ERROR plugin-display configs:" + configs[i]);
continue;
}
if ("pos".equals(values[0])) {
final String[] locations = values[1].split(DisplayConfig.SEPARATOR_VALUE);
displayConfig.pos = Integer.parseInt(locations[0]);
if (1 < locations.length) {
displayConfig.secondPos = Integer.parseInt(locations[1]);
} else {
TwsLog.w(TAG, "Plugin:" + packageName
+ " No configuration secondary location information!!!");
}
} else if ("title".equals(values[0])) {
displayConfig.title = values[1].trim();
} else if ("ctyle".equals(values[0]) || "ctype".equals(values[0])) {// 手贱,之前的写错了
displayConfig.contentType = Integer.parseInt(values[1]);
} else if ("content".equals(values[0])) {
displayConfig.content = values[1].trim();
} else if ("icon".equals(values[0])) {
displayConfig.iconResName = values[1].trim(); // 不在这里解析图片,统一放到需要的时候在解析
} else if ("statkey".equals(values[0])) {
displayConfig.statKey = values[1].trim();
} else if ("ab-title".equals(values[0])) {
displayConfig.ab_title = values[1].trim();
} else if ("ab-rbtnrestype".equals(values[0])) {
displayConfig.ab_rbtnrestype = Integer.parseInt(values[1]);
} else if ("ab-rbtnres".equals(values[0])) {
displayConfig.ab_rbtnres = values[1].trim();
} else if ("ab-rbtncontent".equals(values[0])) {
displayConfig.ab_rbtncontent = values[1].trim();
} else if ("ab-rbtnctype".equals(values[0])) {
displayConfig.ab_rbtnctype = Integer.parseInt(values[1]);
} else if ("dependOn".equals(values[0])) {
ArrayList<String> dependOns = desciptor.getDependOns();
if (null == dependOns) {
dependOns = new ArrayList<String>();
desciptor.setDependOns(dependOns);
}
if (!dependOns.contains(values[1])) {
dependOns.add(values[1].trim());
}
}
}
displayConfig.printf();
displayConfigs.add(displayConfig);
}
} else if ("uses-sdk".equals(tag)) {
String minSdkVersion = parser.getAttributeValue(namespaceAndroid, "minSdkVersion");
String targetSdkVersion = parser.getAttributeValue(namespaceAndroid, "targetSdkVersion");
desciptor.setMinSdkVersion(minSdkVersion);
desciptor.setTargetSdkVersion(targetSdkVersion);
} else if ("meta-data".equals(tag)) {
String name = parser.getAttributeValue(namespaceAndroid, "name");
String value = parser.getAttributeValue(namespaceAndroid, "value");
if (name != null) {
// HashMap<String, String> metaData =
// desciptor.getMetaData();
// if (metaData == null) {
// metaData = new HashMap<String, String>();
// desciptor.setMetaData(metaData);
// }
// if (value != null && value.startsWith("@") &&
// value.length() == 9) {
// String idHex = value.replace("@", "");
// try {
// int id = Integer.parseInt(idHex, 16);
// value = Integer.toString(id);
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// metaData.put(name, value);
TwsLog.d(TAG, "meta-data name=" + name + " value=" + value);
}
} else if ("exported-fragment".equals(tag)) {
String name = parser.getAttributeValue(namespaceAndroid, "name");
String value = parser.getAttributeValue(namespaceAndroid, "value");
value = getName(value, packageName);
if (name != null) {
HashMap<String, String> fragments = desciptor.getFragments();
if (fragments == null) {
fragments = new HashMap<String, String>();
desciptor.setfragments(fragments);
}
fragments.put(name, value);
TwsLog.d(TAG, "fragments.put name:" + name + " value:" + value);
}
} else if ("exported-service".equals(tag)) {
String name = parser.getAttributeValue(namespaceAndroid, "name");
String value = parser.getAttributeValue(namespaceAndroid, "value");
String iface = parser.getAttributeValue(namespaceAndroid, "label");
value = getName(value, packageName);
if (iface != null) {
value = value + "|" + iface;
}
if (name != null) {
HashMap<String, String> functions = desciptor.getFunctions();
if (functions == null) {
functions = new HashMap<String, String>();
desciptor.setFunctions(functions);
}
functions.put(name, value);
TwsLog.d(TAG, "functions.put name:" + name + " value:" + value);
}
} else if ("uses-library".equals(tag)) {
String name = parser.getAttributeValue(namespaceAndroid, "name");
if (dependencies == null) {
dependencies = new ArrayList<String>();
}
dependencies.add(name);
} else if ("application".equals(tag)) {
String applicationName = parser.getAttributeValue(namespaceAndroid, "name");
if (applicationName == null) {
applicationName = Application.class.getName();
}
applicationName = getName(applicationName, packageName);
desciptor.setApplicationName(applicationName);
desciptor.setDescription(parser.getAttributeValue(namespaceAndroid, "label"));
// 这里不解析主题,后面会通过packageManager查询
TwsLog.d(TAG,
"applicationName=" + applicationName + " Description=" + desciptor.getDescription());
} else if ("activity".equals(tag)) {
String windowSoftInputMode = parser.getAttributeValue(namespaceAndroid, "windowSoftInputMode");// strin
String hardwareAccelerated = parser.getAttributeValue(namespaceAndroid, "hardwareAccelerated");// int
// string
String launchMode = parser.getAttributeValue(namespaceAndroid, "launchMode");// string
String screenOrientation = parser.getAttributeValue(namespaceAndroid, "screenOrientation");// string
String theme = parser.getAttributeValue(namespaceAndroid, "theme");// int
String immersive = parser.getAttributeValue(namespaceAndroid, "immersive");// int
// string
String uiOptions = parser.getAttributeValue(namespaceAndroid, "uiOptions");// int
// string
String configChanges = parser.getAttributeValue(namespaceAndroid, "configChanges");// int
// string
HashMap<String, ArrayList<PluginIntentFilter>> map = desciptor.getActivitys();
if (map == null) {
map = new HashMap<String, ArrayList<PluginIntentFilter>>();
desciptor.setActivitys(map);
}
String name = addIntentFilter(map, packageName, namespaceAndroid, parser, "activity");
HashMap<String, PluginActivityInfo> infos = desciptor.getActivityInfos();
if (infos == null) {
infos = new HashMap<String, PluginActivityInfo>();
desciptor.setActivityInfos(infos);
}
PluginActivityInfo pluginActivityInfo = infos.get(name);
if (pluginActivityInfo == null) {
pluginActivityInfo = new PluginActivityInfo();
infos.put(name, pluginActivityInfo);
}
pluginActivityInfo.setHardwareAccelerated(hardwareAccelerated);
pluginActivityInfo.setImmersive(immersive);
if (launchMode == null) {
launchMode = String.valueOf(ActivityInfo.LAUNCH_MULTIPLE);
}
pluginActivityInfo.setLaunchMode(launchMode);
pluginActivityInfo.setName(name);
pluginActivityInfo.setScreenOrientation(screenOrientation);
pluginActivityInfo.setTheme(theme);
pluginActivityInfo.setWindowSoftInputMode(windowSoftInputMode);
pluginActivityInfo.setUiOptions(uiOptions);
if (configChanges != null) {
pluginActivityInfo.setConfigChanges(Integer.parseInt(configChanges.replace("0x", ""), 16));
}
} else if ("receiver".equals(tag)) {
HashMap<String, ArrayList<PluginIntentFilter>> map = desciptor.getReceivers();
if (map == null) {
map = new HashMap<String, ArrayList<PluginIntentFilter>>();
desciptor.setReceivers(map);
}
addIntentFilter(map, packageName, namespaceAndroid, parser, "receiver");
} else if ("service".equals(tag)) {
String process = parser.getAttributeValue(namespaceAndroid, "process");
HashMap<String, ArrayList<PluginIntentFilter>> map = desciptor.getServices();
if (map == null) {
map = new HashMap<String, ArrayList<PluginIntentFilter>>();
desciptor.setServices(map);
}
String name = addIntentFilter(map, packageName, namespaceAndroid, parser, "service");
if (process != null) {
if (TextUtils.isEmpty(pluginProcessName)) {
pluginProcessName = ProcessUtil.getPluginProcessName(PluginLoader.getApplication());
if (TextUtils.isEmpty(pluginProcessName)) {
pluginProcessName = ProcessUtil.getHostProcessName(); // rick_Note潜规则:当前插件和宿主是一个进程
}
}
// 只要配置的不是插件进程就需要配置process属性
if (!process.equals(pluginProcessName)) {
HashMap<String, String> processInfos = desciptor.getServiceProcessInfos();
if (processInfos == null) {
processInfos = new HashMap<String, String>();
desciptor.setServiceProcessInfos(processInfos);
}
processInfos.put(name, process);
}
}
} else if ("provider".equals(tag)) {
String name = parser.getAttributeValue(namespaceAndroid, "name");
name = getName(name, packageName);
String author = parser.getAttributeValue(namespaceAndroid, "authorities");
String exported = parser.getAttributeValue(namespaceAndroid, "exported");
HashMap<String, PluginProviderInfo> providers = desciptor.getProviderInfos();
if (providers == null) {
providers = new HashMap<String, PluginProviderInfo>();
desciptor.setProviderInfos(providers);
}
PluginProviderInfo info = new PluginProviderInfo();
info.setName(name);
info.setExported(Boolean.getBoolean(exported));
info.setAuthority(author);
providers.put(name, info);
}
break;
}
case XmlPullParser.END_TAG: {
break;
}
}
eventType = parser.next();
} while (eventType != XmlPullParser.END_DOCUMENT);
desciptor.setEnabled(true);
// 有可能没有配置application节点,这里需要检查一下application
if (desciptor.getApplicationName() == null) {
desciptor.setApplicationName(Application.class.getName());
}
if (dependencies != null) {
desciptor.setDependencies((String[]) dependencies.toArray(new String[0]));
}
return desciptor;
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static String addIntentFilter(HashMap<String, ArrayList<PluginIntentFilter>> map, String packageName,
String namespace, XmlPullParser parser, String endTagName) throws XmlPullParserException, IOException {
int eventType = parser.getEventType();
String activityName = parser.getAttributeValue(namespace, "name");
activityName = getName(activityName, packageName);
ArrayList<PluginIntentFilter> filters = map.get(activityName);
if (filters == null) {
filters = new ArrayList<PluginIntentFilter>();
map.put(activityName, filters);
}
PluginIntentFilter intentFilter = new PluginIntentFilter();
do {
switch (eventType) {
case XmlPullParser.START_TAG: {
String tag = parser.getName();
if ("intent-filter".equals(tag)) {
intentFilter = new PluginIntentFilter();
filters.add(intentFilter);
} else {
intentFilter.readFromXml(tag, parser);
}
}
}
eventType = parser.next();
} while (!endTagName.equals(parser.getName()));// 再次到达,表示一个标签结束了
return activityName;
}
private static String getName(String nameOrig, String pkgName) {
if (nameOrig == null) {
return null;
}
StringBuilder sb = null;
if (nameOrig.startsWith(".")) {
sb = new StringBuilder();
sb.append(pkgName);
sb.append(nameOrig);
} else if (!nameOrig.contains(".")) {
sb = new StringBuilder();
sb.append(pkgName);
sb.append('.');
sb.append(nameOrig);
} else {
return nameOrig;
}
return sb.toString();
}
}