/*
* Copyright (C) 2014 The AppCan Open Source Project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.zywx.wbpalmstar.widgetone.dataservice;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.zywx.wbpalmstar.base.BUtility;
import org.zywx.wbpalmstar.base.zip.CnZipInputStream;
import org.zywx.wbpalmstar.base.zip.ZipEntry;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.text.TextUtils;
public class WidgetPackageMgr {
public static final String WIDGET_CONFIG_FILE_NAME = "config.xml";
public static final String WIDGET_CONFIG_PARENT_FILE_NAME = "widget";
public static final String DYNAMIC_PLUGIN_ASSEST_PATH_NAME = "plugin";
public static final String DYNAMIC_PLUGIN_SANDBOX_PATH_NAME = "apkfile";
public final static String SP_WIDGET_ONE_CONFIG = "widgetOneConfig";
/**
* 安装主应用补丁包
*
* @param context
* @param appId
* @param installType:安装补丁包类型1:网页包;2:插件包;3:网页和插件
* @return 安装成功,返回版本号;失败,返回空。
*/
public static String installWidgetPatch(Context context, String appId,
int installType) {
WidgetPatchConfig wConfig = getWidgetPatchConfig(context, appId,
WDataManager.m_sboxPath + "widget/");
if (wConfig.hasZip) {
unZip(context, wConfig.isDynamicLoad, appId, installType);
}
return wConfig.version;
}
/**
* 安装子应用(包括全量包、补丁包)
*
* @param appId
* @param filePath
* @param desPath
* @param encoding
* @return 安装路径
*/
public static String installSubWidget(String appId, String filePath,
String desPath, String encoding) {
String installPath = "";
boolean isDynamic = isDynamicSubWidget(appId, filePath);
if (!isDynamic) {
installPath = unZip(filePath, desPath, null);
} else {
String pluginPath = WDataManager.m_sboxPath
+ DYNAMIC_PLUGIN_SANDBOX_PATH_NAME + File.separator;
installPath = unZipDynamic(appId, filePath,
desPath + File.separator + appId, pluginPath, encoding,
BUtility.INSTALL_PATCH_ALL);
}
return installPath;
}
/**
* 解压补丁包
*
* @param context
* @param appId
* @param installType:安装补丁包类型1:网页包;2:插件包;3:网页和插件
* @return
*/
public static boolean unZip(Context context, String appId,
int installType) {
WidgetPatchConfig wConfig = getWidgetPatchConfig(context, appId,
WDataManager.m_sboxPath + "widget/");
return unZip(context, wConfig.isDynamicLoad, appId, installType);
}
static class WidgetPatchConfig {
boolean hasZip;
String version;
boolean isDynamicLoad;
}
/**
* @param context
* @param sboxPath
* @param appId
* @return
*/
public static boolean isHasUpdateZip(Context context, String sboxPath,
String appId) {
return getWidgetPatchConfig(context, appId,
sboxPath + "widget/").hasZip;
}
private static boolean isDynamicSubWidget(String appId, String widgetPath) {
boolean isDynamic = true;
FileInputStream inputStream = null;
CnZipInputStream in = null;
try {
inputStream = new FileInputStream(widgetPath);
in = new CnZipInputStream(inputStream, "UTF-8");
ZipEntry entry = in.getNextEntry();
String configName = appId + File.separator
+ WIDGET_CONFIG_FILE_NAME;
while (entry != null) {
String zename = entry.getName();
if (configName.equals(zename)) {
isDynamic = false;
break;
}
entry = in.getNextEntry();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return isDynamic;
}
/**
* 判断是否有增量更新包,如果有,继续判断版本号是否大于当前APK的版本号。 主应用补丁包结构:旧:网页代码+config.xml打包。
* 动态加载结构:widget/appId/网页代码+config.xml。
*
* @param context
* @param appId
* @param zipPath
* @return
*/
private static WidgetPatchConfig getWidgetPatchConfig(Context context,
String appId, String zipPath) {
WidgetPatchConfig wConfig = new WidgetPatchConfig();
wConfig.version = "";
SharedPreferences preferences = context
.getSharedPreferences("updateInfo", Context.MODE_PRIVATE);
int totalSize = preferences.getInt("totalSize", 0);
int downloaded = preferences.getInt("downloaded", 0);
if (totalSize == 0 || downloaded == 0 || totalSize != downloaded) {
wConfig.hasZip = false;
} else {
String filePath = preferences.getString("filePath", null);
if (!TextUtils.isEmpty(filePath)) {
try {
File dir = new File(zipPath);
// 建立与目标文件的输入连接
FileInputStream inputStream = new FileInputStream(filePath);
CnZipInputStream in = new CnZipInputStream(inputStream,
"UTF-8");
ZipEntry entry = in.getNextEntry();
byte[] c = new byte[1024];
int slen;
String dynamicConfigStart = WIDGET_CONFIG_PARENT_FILE_NAME
+ File.separator + appId + File.separator;
while (entry != null) {
String zename = entry.getName();
if (WIDGET_CONFIG_FILE_NAME.equals(zename)
|| (dynamicConfigStart
+ WIDGET_CONFIG_FILE_NAME)
.equals(zename)) {
wConfig.isDynamicLoad = WIDGET_CONFIG_FILE_NAME
.equals(zename) ? false : true;
if (wConfig.isDynamicLoad) {
zename = zename
.substring(dynamicConfigStart.length());
}
File files = new File(
dir.getAbsolutePath() + "/" + zename)
.getParentFile();// 当前文件所在目录
if (!files.exists()) {// 如果目录文件夹不存在,则创建
files.mkdirs();
}
// 得到config.xml文件的内容
String configPathTmp = dir.getAbsolutePath() + "/tmp/" + zename;
File configDirTmp = new File(configPathTmp).getParentFile();
if (!configDirTmp.exists()) {// 如果目录文件夹不存在,则创建
configDirTmp.mkdirs();
}
File configFileTmp = new File(configPathTmp);
FileOutputStream out = new FileOutputStream(configFileTmp);
while ((slen = in.read(c, 0, c.length)) != -1)
out.write(c, 0, slen);
// 对config.xml文件进行XML解析
if (configFileTmp.exists()) {
FileInputStream input = new FileInputStream(
configFileTmp);
// 得到增量更新包config.xml文件中的版本号
String m_verString = BUtility.parserXmlLabel(
input, "config", "widget", "version");
wConfig.version = m_verString;
// 比较增量更新包和当前APK的版本号大小
String dbVerString = context
.getSharedPreferences(
SP_WIDGET_ONE_CONFIG,
Context.MODE_PRIVATE)
.getString("dbVer", null);
if (m_verString != null
&& dbVerString != null) {
// 格式化版本号内容,去掉"."
m_verString = formatVerString(
m_verString.split("\\."));
dbVerString = formatVerString(
dbVerString.split("\\."));
// 转换成long型
long m_verLong = Long
.parseLong(m_verString);
long dbVerLong = Long
.parseLong(dbVerString);
if (m_verLong > dbVerLong) {
wConfig.hasZip = true;
}
}
input.close();
configFileTmp.delete();
} else {
wConfig.hasZip = false;
}
out.close();
configDirTmp.delete();
break;
}
entry = in.getNextEntry();
}
in.close();
} catch (Exception i) {
wConfig.hasZip = false;
}
} else {
wConfig.hasZip = false;
}
}
return wConfig;
}
private static String formatVerString(String[] s) {
if (s.length == 1 && s[0].length() == 1) {
s[0] = 0 + s[0];
}
if (s.length == 2 && s[1].length() == 1) {
s[1] = "0" + s[1];
}
if (s.length == 3 && s[2].length() == 1) {
s[2] = "000" + s[2];
}
if (s.length == 3 && s[2].length() == 2) {
s[2] = "00" + s[2];
}
if (s.length == 3 && s[2].length() == 3) {
s[2] = "0" + s[2];
}
StringBuffer sbf = new StringBuffer("");
if (s.length == 1) {
sbf.append(s[0]).append("000000");
} else if (s.length == 2) {
sbf.append(s[0]).append(s[1]).append("0000");
} else if (s.length == 3) {
sbf.append(s[0]).append(s[1]).append(s[2]);
}
return sbf.toString();
}
/**
* 解压补丁包
*
* @param context
* @param isDynamic
* @param sboxPath
* @param appId
* @param installType:安装补丁包类型1:网页包;2:插件包;3:网页和插件
* @return
*/
private static boolean unZip(Context context, boolean isDynamic,
String appId, int installType) {
boolean unZip = false;
SharedPreferences preferences = context
.getSharedPreferences("updateInfo", Context.MODE_PRIVATE);
int totalSize = preferences.getInt("totalSize", 0);
int downloaded = preferences.getInt("downloaded", 0);
if (totalSize == 0 || downloaded == 0 || totalSize != downloaded) {
return false;
}
String filePath = preferences.getString("filePath", null);
if (TextUtils.isEmpty(filePath)) {
return false;
}
if (BUtility.PATCH_PLUGIN_FLAG != installType) {
Editor editor = preferences.edit();
editor.clear();
editor.commit();
}
String widgetPath = WDataManager.m_sboxPath + "widget/";
if (!isDynamic) {
unZip = (!TextUtils.isEmpty(unZip(filePath, widgetPath, null)));
} else {
String pluginPath = WDataManager.m_sboxPath
+ DYNAMIC_PLUGIN_SANDBOX_PATH_NAME + File.separator;
unZip = (!TextUtils.isEmpty(unZipDynamic(appId, filePath,
widgetPath, pluginPath, null, installType)));
}
if (unZip && (BUtility.PATCH_PLUGIN_FLAG != installType)) {
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
}
return unZip;
}
/**
* 解压补丁包
*
* @param inputStream
* @param decompression
* @param encoding
* @return
*/
private static String unZip(String srcPath, String decompression,
String encoding) {
if (encoding == null || encoding.equals("")) {
encoding = "UTF-8";
}
String filePath = "";
CnZipInputStream in = null;
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(srcPath);
// 建立与目标文件的输入连接
in = new CnZipInputStream(inputStream, encoding);
ZipEntry file = in.getNextEntry();
byte[] c = new byte[1024];
String dpPath = new File(decompression).getAbsolutePath();
filePath = dpPath + "/" + file.getName();
while (file != null) {
String zename = file.getName();
getFileFromZip(in, file, c, dpPath, zename);
file = in.getNextEntry();
}
} catch (Exception i) {
i.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return filePath;
}
/**
* 动态加载插件解压补丁包
*
* @param context
* @param appId
* @param inputStream
* @param widgetPath
* @param pluginPath
* @param encoding
* @param installType:安装补丁包类型1:网页包;2:插件包;3:网页和插件
* @return
*/
private static String unZipDynamic(String appId, String srcPath,
String widgetPath, String pluginPath, String encoding,
int installType) {
if (encoding == null || encoding.equals("")) {
encoding = "UTF-8";
}
String widgetAbsolutePath = new File(widgetPath).getAbsolutePath();
String pluginAbsolutePath = new File(pluginPath).getAbsolutePath();
CnZipInputStream in = null;
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(srcPath);
in = new CnZipInputStream(inputStream, encoding);
ZipEntry file = in.getNextEntry();
byte[] c = new byte[1024];
String widgetPathStart = WIDGET_CONFIG_PARENT_FILE_NAME
+ File.separator + appId + File.separator;
String pluginPathStart = DYNAMIC_PLUGIN_ASSEST_PATH_NAME
+ File.separator;
while (file != null) {
String decompression = "";
String startStr = "";
String zename = file.getName();
if (zename.startsWith(widgetPathStart)
&& ((installType & BUtility.PATCH_WIDGET_FLAG) != 0)) {
decompression = widgetAbsolutePath;
startStr = widgetPathStart;
} else if (zename.startsWith(pluginPathStart)
&& ((installType & BUtility.PATCH_PLUGIN_FLAG) != 0)) {
decompression = pluginAbsolutePath;
startStr = pluginPathStart;
}
if (!TextUtils.isEmpty(decompression)) {
zename = zename.substring(startStr.length());
getFileFromZip(in, file, c, decompression, zename);
}
file = in.getNextEntry();
}
} catch (Exception i) {
i.printStackTrace();
widgetAbsolutePath = "";
} finally {
try {
if (in != null) {
in.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return widgetAbsolutePath;
}
/**
* 从zip包中获取文件内容
*
* @param in
* @param file
* @param c
* @param decompression
* @param zename
* @throws FileNotFoundException
* @throws IOException
*/
private static void getFileFromZip(CnZipInputStream in, ZipEntry file,
byte[] c, String decompression, String zename)
throws FileNotFoundException, IOException {
int slen;
try {
if (file.isDirectory()) {
File files = new File(decompression + "/" + zename); // 在指定解压路径下建子文件夹
files.mkdirs();// 新建文件夹
} else {
File files = new File(decompression + "/" + zename)
.getParentFile();// 当前文件所在目录
if (!files.exists()) {// 如果目录文件夹不存在,则创建
files.mkdirs();
}
FileOutputStream out = new FileOutputStream(
decompression + "/" + zename);
while ((slen = in.read(c, 0, c.length)) != -1) {
out.write(c, 0, slen);
}
out.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}