/*
Copyright 2012-2013, Polyvi Inc. (http://polyvi.github.io/openxface)
This program is distributed under the terms of the GNU General Public License.
This file is part of xFace.
xFace is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
xFace 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with xFace. If not, see <http://www.gnu.org/licenses/>.
*/
package com.polyvi.xface;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import android.content.Context;
import com.polyvi.xface.ams.XAMSComponent;
import com.polyvi.xface.app.XAppInfo;
import com.polyvi.xface.core.XConfiguration;
import com.polyvi.xface.core.XRuntime;
import com.polyvi.xface.plugin.api.XPluginLoader;
import com.polyvi.xface.util.XAppUtils;
import com.polyvi.xface.util.XConstant;
import com.polyvi.xface.util.XFileUtils;
import com.polyvi.xface.util.XLog;
import com.polyvi.xface.util.XLogController;
import com.polyvi.xface.util.XSocketLog;
import com.polyvi.xface.util.XXmlUtils;
/**
* Player工程 系统启动的实现 目前主要负责:启动一个player app
*/
public class XPlayerSystemBootstrap implements XSystemBootstrap {
private static final String TAG_START_APP_ID = "app";
private static final String CLASS_NAME = XPlayerSystemBootstrap.class
.getSimpleName();
private static final String PLUGIN_JAR_DIR_NAME = "plugin_jar";
private static String HOST_PORT = "8018";
private static int TIME_OUT = 2000;
private static final String INDEX_HTML_DIR_NAME = "index.html";
private XFaceMainActivity mContext;
private String mHostIp;
public XPlayerSystemBootstrap(XFaceMainActivity context) {
mContext = context;
}
@Override
public void prepareWorkEnvironment() {
initLogger();
}
/**
* 配置要启动的app文件
*
*/
private void configStartApp() {
// 先从服务器上获取启动app
if (configStartAppFromSever()) {
return;
}
// 服务器上获取启动app不成功,再检查本地app是否存在
String indexPath = getIndexPath();
if (XFileUtils.checkFileExist(indexPath)) {
return;
}
// 如果本地app不存在,则设置默认的显示页面,防止应用停止到splash界面
copyAssetsToTargetPath(mContext, INDEX_HTML_DIR_NAME, indexPath);
}
/**
* 从服务器请求startapp
*
* @return 从服务器获取的本地文件的地址
*/
private String requestAppFromServer() {
String ret = null;
if (null == mHostIp) {
// 没有配置IP 请求文件失败
return ret;
}
FileOutputStream fos = null;
String localFilePath = XConfiguration.getInstance().getWorkDirectory()
+ File.separator + "startapp.zip";
try {
// FIXME: 网络部分迁移到XNetworkUtils中去
URL url = new URL("http://" + mHostIp + ":" + HOST_PORT
+ "/app.zip");
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setConnectTimeout(TIME_OUT);
connection.setRequestMethod("GET");
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
fos = new FileOutputStream(localFilePath);
InputStream is = connection.getInputStream();
byte[] buffer = new byte[XConstant.BUFFER_LEN * 8];
int length = -1;
while ((length = is.read(buffer)) != -1) {
fos.write(buffer, 0, length);
}
fos.close();
fos = null;
// 从服务器获取文件成功
ret = localFilePath;
}
} catch (ProtocolException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
if (null != fos) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return ret;
}
/**
* jar包的加载目录
*
* @return
*/
private String getJarFolder() {
XConfiguration config = XConfiguration.getInstance();
File f = new File(config.getWorkDirectory() + File.separator
+ PLUGIN_JAR_DIR_NAME);
if (!f.exists()) {
f.mkdirs();
}
return f.getAbsolutePath();
}
/**
* 创建app management
*
* @param runtime
* @return
*/
private XAMSComponent createAMSComponent(XRuntime runtime) {
XAMSComponent amsCom = new XAMSComponent(mContext,
runtime.getAppFactory());
return amsCom;
}
/**
* 1. 启动一个player app(player app从固定的位置load离散文件)
*/
@Override
public void boot(final XRuntime runtime) {
XPluginLoader.setJarDir(getJarFolder(), getJarFolder());
initPlayerApp(runtime);
new Thread() {
@Override
public void run() {
handlePresetAppPackage(runtime.getStartApp().getWorkSpace());
File appDir = new File(XConfiguration.getInstance()
.getAppInstallDir(), TAG_START_APP_ID);
XFileUtils.copyEmbeddedJsFile(mContext,
appDir.getAbsolutePath());
XFileUtils.createNoMediaFileInWorkDir();
configStartApp();
mContext.runOnUiThread(new Runnable() {
public void run() {
runtime.runStartApp(null);
}
});
}
}.start();
}
/**
* 初始化 player app
*
* @param ams
* AppManagement对象
* */
protected void initPlayerApp(XRuntime runtime) {
if (!runtime.initStartApp(getStartAppInfo())) {
return;
}
final XAMSComponent ams = createAMSComponent(runtime);
ams.markPortal(runtime.getStartApp());
}
/**
* 读取startapp的app.xml并获取其应用信息
*
* @return appInfo
*/
private XAppInfo getStartAppInfo() {
XAppInfo startAppInfo = null;
try {
File file = new File(XConfiguration.getInstance()
.getAppInstallDir()
+ TAG_START_APP_ID
+ File.separator
+ XConstant.APP_CONFIG_FILE_NAME);
startAppInfo = XAppUtils.parseAppXml(new FileInputStream(file));
} catch (FileNotFoundException e) {
XLog.w(CLASS_NAME, "app.xml not found!");
}
// 如果读取失败,则安装默认方式初始化appInfo
if (null == startAppInfo) {
startAppInfo = new XAppInfo();
startAppInfo.setType("xapp");
}
startAppInfo.setAppId(TAG_START_APP_ID);
startAppInfo.setSrcRoot(XConstant.FILE_SCHEME
+ XConfiguration.getInstance().getAppInstallDir()
+ TAG_START_APP_ID);
return startAppInfo;
}
private void handlePresetAppPackage(String defaultAppWorkspace) {
String workDir = XConfiguration.getInstance().getWorkDirectory();
File presetDir = new File(workDir,
XConstant.PRE_SET_APP_PACKAGE_DIR_NAME);
if (presetDir.exists()) {
File defAppWorkspacePresetDir = new File(defaultAppWorkspace,
XConstant.PRE_SET_APP_PACKAGE_DIR_NAME);
// 在默认app的workspace下面创建"pre_set"目录用于存放预置包
if (!defAppWorkspacePresetDir.exists()) {
defAppWorkspacePresetDir.mkdirs();
}
// 列出pre_set里面的预置包
String[] presetAppNames = presetDir.list();
for (String appName : presetAppNames) {
File oldApp = new File(presetDir.getAbsolutePath(), appName);
File newApp = new File(
defAppWorkspacePresetDir.getAbsolutePath(), appName);
if (newApp.exists()) {
// 如果存在预置包,则将原来的预置包删除
newApp.delete();
}
boolean success = oldApp.renameTo(newApp);
if (!success) {
XLog.e(CLASS_NAME, "copy pre set app package : " + appName
+ " fail!");
}
}
// 目前的实现是拷贝结束后删除xface的工作空间里面的预置包目录,以后决定是否需要删除
XFileUtils.deleteFileRecursively(presetDir.getAbsolutePath());
}
}
/**
* 从debug.xml中读取socket连接服务器的ip地址
*
* @return 返回从debug.xml中读取到的ip地址
*/
private String readIpFromConfig() {
/** 配置文件debug.xml内容对应的Document对象 */
Document Document;
String xmlPath = XConfiguration.getInstance().getWorkDirectory()
+ XSocketLog.DEBUG_CONFIG;
File debugXml = new File(xmlPath);
String hostIP = null;
if (debugXml.exists()) {
Document = XXmlUtils.parseXml(xmlPath);
NodeList nodeList = Document
.getElementsByTagName(XSocketLog.TAG_SOCKETLOG);
if (null == nodeList || 0 == nodeList.getLength()) {
return null;
}
Element socketLogElement = (Element) nodeList.item(0);
hostIP = socketLogElement.getAttribute(XSocketLog.ATTR_HOST_IP);
}
mHostIp = hostIP;
return hostIP;
}
/**
* 根据是否获取到socket服务器ip地方来对XLog进行初始化
*/
private void initLogger() {
String hostIP = readIpFromConfig();
if (null != hostIP) {
XLogController.setIP(hostIP);
}
}
/**
* 从PC服务器请求app文件 return true 成功 false 失败
*/
private boolean configStartAppFromSever() {
// TODO:增量请求 加快player的请速度
String localFile = requestAppFromServer();
if (null == localFile) {
XLog.i(CLASS_NAME, "Get AppPath From Server Fail!");
return false;
}
// 解压到player指定的位置
if (!XFileUtils.unzipFile(XConfiguration.getInstance()
.getAppInstallDir() + TAG_START_APP_ID, localFile)) {
XLog.e(CLASS_NAME, "unzip App From Server Fail!");
return false;
}
return new File(localFile).delete();
}
/**
* 获取player的index路径
*
* @return index路径
*/
private String getIndexPath() {
String appInstallDir = XConfiguration.getInstance().getAppInstallDir();
return (appInstallDir + TAG_START_APP_ID + File.separator + INDEX_HTML_DIR_NAME);
}
/**
* copy assets file to target path
*/
private void copyAssetsToTargetPath(Context context, String fileName,
String targetPath) {
try {
InputStream is = context.getAssets().open(fileName);
XFileUtils.createFileByData(new File(targetPath).getAbsolutePath(),
is);
is.close();
} catch (IOException e) {
XLog.e(CLASS_NAME, e.getMessage());
e.printStackTrace();
}
}
}