/* 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.IOException; import java.io.InputStream; import java.util.HashMap; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.os.Build; import android.os.Bundle; import android.telephony.TelephonyManager; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; import android.widget.TextView; import com.polyvi.xface.configXml.XTagNotFoundException; import com.polyvi.xface.core.XConfiguration; import com.polyvi.xface.core.XISystemContext; import com.polyvi.xface.core.XRuntime; import com.polyvi.xface.event.XEvent; import com.polyvi.xface.event.XEventType; import com.polyvi.xface.event.XSystemEventCenter; import com.polyvi.xface.extension.XActivityResultListener; import com.polyvi.xface.ssl.XSSLManager; import com.polyvi.xface.util.XConstant; import com.polyvi.xface.util.XLog; import com.polyvi.xface.util.XNotification; import com.polyvi.xface.util.XStrings; import com.polyvi.xface.util.XUtils; import com.polyvi.xface.view.XAppWebView; import com.umeng.analytics.MobclickAgent; /** * 该类是android程序的主activity,也是整个程序的入口. * 主要管理整个程序的生命周期以及执行程序的初始化操作 */ /** * TODO:XFaceMainActivity抽象出如下接口: <br/> * 1、getActivity,扩展等其他地方用到Activity(context)都由这个方法获取 <br/> * 2、startActivityForResult与其他类似接口 <br/> * 这样XFaceMainActivity就避免暴露过多的接口,又不用强转。<br/> */ public class XFaceMainActivity extends Activity implements XISystemContext{ private static final String CLASS_NAME = XFaceMainActivity.class.getName(); private static final int ANDROID4_2_API_LEVEL = 17; /** The content view of activity */ private ViewGroup mContentView = null; protected ViewGroup mBootSplashView; private View mAppSplashView; private Bitmap mAppSplashImage; protected TextView mVersionText; protected RelativeLayout.LayoutParams mVersionParams; protected XRuntime mRuntime; private XNotification mWaitingNotification = new XNotification(this);; private HashMap<Integer, XActivityResultListener> mActivityResultListenerMap = null; private XStartParams mStartParams; private XSecurityPolicy mSecurityPolicy; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); systemBoot(); } /** * 初始化系统事件处理器 */ private void initSystemEventCenter() { XSystemEventCenter.init(this); } /** * 创建app安全策略 * @return 安全策略 */ protected XSecurityPolicy createSecurityPolicy() { return new XDefaultSecurityPolicy(this); } /** * 创建系统启动组件 * @return */ protected XSystemBootstrap createSystemBootstrap() { return new XSystemInitializer(this); } /** * 创建管理与https有关证书库对象 */ protected void createSSLManager(){ XSSLManager.createInstance(this); } /** * 创建Configuration对象 */ protected void initConfiguration(){ XConfiguration.getInstance(); } /** * 创建系统运行时对象 */ public XRuntime initRuntime() { mRuntime = new XRuntime(); initActivityResultListenerMap(); mSecurityPolicy = createSecurityPolicy(); mRuntime.init(this); return mRuntime; } /** * 初始化保存XActivityResultListener的Map */ private void initActivityResultListenerMap() { mActivityResultListenerMap = new HashMap<Integer, XActivityResultListener>(); } /** * 程序的入口函数 */ private void systemBoot() { initMobclickAgent(); initSystemEventCenter(); initConfiguration(); XConfiguration.getInstance().loadPlatformStrings(getContext()); mStartParams = XStartParams.parse(getIntent().getStringExtra( XConstant.TAG_APP_START_PARAMS)); // 解析系统配置 try { initSystemConfig(); } catch (IOException e) { this.toast("Loading System Config Failure."); XLog.e(CLASS_NAME, "Loading system config failure!"); e.printStackTrace(); return; } catch (XTagNotFoundException e) { this.toast("Loading System Config Failure."); XLog.e(CLASS_NAME, "parse config.xml error:" + e.getMessage()); e.printStackTrace(); return; } // 配置系统LOG等级 XLog.setLogLevel(XConfiguration.getInstance().readLogLevel()); // 配置系统的工作目录 XConfiguration.getInstance() .configWorkDirectory(this, getWorkDirName()); // 设置window的样式 setWindowStyle(); // 创建view容器 createContentView(); // 根据平台配置决定是否启动splash startBootSplashIfNeeded(); // 根据平台配置决定是否检测更新 checkUpdateIfNeeded(); createSSLManager(); // 系统启动 XSystemBootstrap bootstrap = createSystemBootstrap(); new XPrepareWorkEnvronmentTask(bootstrap, this).execute(); } /** * 解析系统配置 * @throws IOException * @throws XTagNotFoundException */ private void initSystemConfig() throws IOException, XTagNotFoundException { XConfiguration.getInstance().readConfig(this.getAssets().open( XConstant.PRE_INSTALL_SOURCE_ROOT + XConstant.CONFIG_FILE_NAME)); } /** * 获得手机的deviceId * @return */ protected String getKey() { // TODO:用更合理的方式来屏蔽4.2上获取不到deviceId的问题 if (Build.VERSION.SDK_INT == ANDROID4_2_API_LEVEL) { return null; } TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); String deviceID = tm.getDeviceId(); return deviceID; } /** * 设置窗口的一些参数 */ private void setWindowStyle() { getWindow().requestFeature(Window.FEATURE_NO_TITLE); // 根据平台配置决定是否显示标题栏 if (isRequiredFullScreen()) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } } /** * 添加一个子视图到Activity的content view,如果view是可见的,则view会被显示在屏幕上 * @param view * 子视图 */ public void addView(XAppWebView view) { if (view instanceof View) { View subView = (View) view; subView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); mContentView.addView(subView); } } /** * 从Activity的content view中remove掉一个子视图 * @param view * 子视图 */ public void removeView(XAppWebView view) { if (view instanceof View) { mContentView.removeView((View) view); } } @Override protected void onPause() { super.onPause(); XEvent evt = XEvent.createEvent(XEventType.PAUSE); XSystemEventCenter.getInstance().sendEventSync(evt); MobclickAgent.onPause(this); } @Override public void finish() { super.finish(); } @Override protected void onResume() { super.onResume(); XEvent evt = XEvent.createEvent(XEventType.RESUME); XSystemEventCenter.getInstance().sendEventSync(evt); MobclickAgent.onResume(this); } /** * 获取工作目录的名字 * @return */ protected String getWorkDirName() { String packageName = getPackageName(); String workDir = XConfiguration.getInstance().getWorkDirectory(this, packageName); return workDir; } /** * 是否需要显示splash * @return */ private boolean isRequiredSplash() { return XConfiguration.getInstance().readShowSplash(); } /** * 是否需要显示标题栏 */ private boolean isRequiredFullScreen() { return XConfiguration.getInstance().readFullscreen(); } /** * 创建内容view */ private void createContentView() { mContentView = new RelativeLayout(this); mContentView.setBackgroundColor(Color.WHITE); setContentView(mContentView); } /** * 启动时如果已配置则显示默认的SplashScreen图片 * @param imagePath * 图片的绝对路径 */ public void startBootSplashIfNeeded() { // TODO:将splash做成动态的,资源拷贝,解压,app预装等放在后台进行 if (!isRequiredSplash()) { return; } startBootSplash(); } /** * 检测更新 */ public void checkUpdateIfNeeded() { // 判断是否需要检测更新 if (XConfiguration.getInstance().readUpdateCheck()) { // 执行需要检测更新 String address = XConfiguration.getInstance().readUpdateAddress(); if (null != address) { (new XApkUpdater(this)).execute(address); } else { XLog.e("error", "please set the update address"); } } } @Override public void startBootSplash() { if (mBootSplashView != null) { mContentView.addView(mBootSplashView); return; } mBootSplashView = new RelativeLayout(this); LayoutParams layoutParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); mBootSplashView.setLayoutParams(layoutParams); ImageView view = new ImageView(this); view.setBackgroundResource(R.drawable.xface_logo); mBootSplashView.addView(view, layoutParams); if (null != mVersionText && null != mVersionParams) { mBootSplashView.addView(mVersionText, mVersionParams); } mContentView.addView(mBootSplashView); } @Override public void stopBootSplash() { if (mBootSplashView != null) { try { String time = XConfiguration.getInstance().readSplashDelay(); if (null == time) { XLog.w(CLASS_NAME, "Please config splash delay in config.xml!"); } else { Thread.sleep(Integer.parseInt(time)); } } catch (InterruptedException e) { e.printStackTrace(); } mBootSplashView.removeAllViews(); mContentView.removeView(mBootSplashView); mBootSplashView = null; setBootWebViewVisible(); } } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { boolean doDefaultAction = true; // 对一些键进行处理特殊,例如back键 if (needKeyUpEventBeNotified(keyCode, event)) { doDefaultAction = mRuntime.getKeyListener().onKeyUp(keyCode, event); } return doDefaultAction ? super.onKeyUp(keyCode, event) : true; } /** * 判断keyup event是否需要被通知到引擎中 */ private boolean needKeyUpEventBeNotified(int keyCode, KeyEvent event) { /* * 对于back键,如果不是在引擎处于活动状态时按下(key down和key up要配对), * 例如关闭软键盘和其他Activity的back键,就不通知引擎。 */ return (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking() && !event .isCanceled()) || keyCode != KeyEvent.KEYCODE_BACK; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { boolean doDefaultAction = true; if (null != mRuntime.getKeyListener()) { doDefaultAction = mRuntime.getKeyListener().onKeyDown(keyCode, event); } else { XLog.e("error", "runtime is not init!"); } // 对于back键,在父类方法中会调用event.startTracking(); return doDefaultAction ? super.onKeyDown(keyCode, event) : true; } @Override public void onDestroy() { super.onDestroy(); XEvent evt = XEvent.createEvent(XEventType.DESTROY); XSystemEventCenter.getInstance().sendEventSync(evt); System.exit(0); } /** * 启动一个activity,当此activity退出时会调用onActivityResult * @param listener * 用于确认在哪一个扩展中调用此方法 * @param requestCode * 传人参数 * @param intent * 启动Activity的intent */ @Override public void startActivityForResult(XActivityResultListener listener, Intent intent, int requestCode) { registerActivityResultListener(requestCode, listener); super.startActivityForResult(intent, requestCode); } @Override public void registerActivityResultListener(int requestCode, XActivityResultListener listener) { mActivityResultListenerMap.put(Integer.valueOf(requestCode), listener); } /** * 由startActivityForResult启动的activity退出时调用 * @param requestCode * startActivityForResult 提供的值 * @param resultCode * 由启动的activity中 setResult确定. * @param intent * 从新的Activity传过来的intent */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); XActivityResultListener callback = mActivityResultListenerMap .get(Integer.valueOf(requestCode)); mActivityResultListenerMap.remove(Integer.valueOf(requestCode)); if (callback != null) { callback.onActivityResult(requestCode, resultCode, intent); } else { XLog.e("error", "XActivityResultListener is null!"); } } /** * 应用中调用显示指定的SplashScreen图片,如果指定的图片为空则加载默认图片 * @param imagePath * 图片的绝对路径 */ public void startAppSplash(String imagePath) { // TODO:将splash做成动态的,资源拷贝,解压,app预装等放在后台进行 stopAppSplash(); mAppSplashView = new RelativeLayout(this); LayoutParams layoutParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); mAppSplashView.setLayoutParams(layoutParams); mAppSplashImage = XUtils.decodeBitmap(imagePath); if (mAppSplashImage == null) { /**如果前面获取到的图片为空,则默认采用xface_logo*/ InputStream is = getActivity().getResources().openRawResource(R.drawable.xface_logo); mAppSplashImage = XUtils.decodeBitmap(is); } mAppSplashView.setBackgroundDrawable(new BitmapDrawable( mAppSplashImage)); mContentView.addView(mAppSplashView); } /** * 应用停止显示SplashScreen图片 */ public void stopAppSplash() { if (mAppSplashView != null) { mContentView.removeView(mAppSplashView); mAppSplashView = null; if (mAppSplashImage != null) { if (!mAppSplashImage.isRecycled()) { mAppSplashImage.recycle(); } mAppSplashImage = null; } } } @Override public boolean isBootSplashShowing() { return mBootSplashView != null; } @Override public Context getContext() { return this; } @Override public void waitingDialogForAppStart() { if (isBootSplashShowing()) { return; } mWaitingNotification.activityStart( XStrings.getInstance() .getString(XStrings.WAITING_MESSAGE_TITLE), XStrings.getInstance().getString( XStrings.WAITING_MESSAGE_CONTENT)); } @Override public void waitingDialogForAppStartFinished() { if (mBootSplashView != null) { /**根据config.xml的配置决定是否自动隐藏SplashScreen*/ if(XConfiguration.getInstance().readAutoHideSplash()) { stopBootSplash(); } return; } mWaitingNotification.activityStop(); } @Override public void toast(String message) { mWaitingNotification.toast(message); } @Override public XStartParams getStartParams() { return mStartParams; } @Override public Activity getActivity() { return this; } /** * 初始化UMeng统计代理 */ protected void initMobclickAgent() { //注意:这里需要将SessionContinue的设置为0,不然连续启动程序的间隔时间果断的话,不会发送数据 MobclickAgent.setSessionContinueMillis(0); MobclickAgent.onError(this); } /** * 设置BootWebView可见 */ private void setBootWebViewVisible() { int childCount = mContentView.getChildCount(); int i = 0; while( i < childCount) { View view = mContentView.getChildAt(i); if(view instanceof XAppWebView) { view.setVisibility(View.VISIBLE); view.requestFocus(); break; } i++; } } @Override public boolean isSplashShowing() { return null != mBootSplashView; } @Override public XSecurityPolicy getSecurityPolicy() { return mSecurityPolicy; } }