package org.bbs.osgi.activity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.bbs.felix.App;
import org.bbs.felix.FelixWrapper;
import org.bbs.osgi.activity.ReflectUtil.ActivityReflectUtil;
import org.bbs.osgi.activity.embed.EmbeddedApplictionAgent;
import org.bbs.osgi.activity.embed.EmbeddedBundleActivity;
import org.bbs.osgi.activity.embed.SimpleActivityAgent;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.wiring.BundleWiring;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
/**
* if android call us we call through to {@link #mActivityStub};
* otherwise call super or do ourself.
*
* <p>
* when add new function, keep it in section, in order.
*
* @author luoqii
*
* @see {@link ActivityAgent}
*/
public class BundleActivity extends
AbsBundleActivity
implements InstrumentationWrapper.CallBack
{
private static final String RES_PATH_APK_RES = "res.apk";
private static final String TAG = BundleActivity.class.getSimpleName();
/**
* type {@link String}
*/
public static final String EXTRA_SERVICE_NAME = ".extra_service_name";
/**
* type {@link String}
*/
public static final String EXTRA_SERVICE_FILTER = ".extra_service_filter_name";
public static final String DEFAULT_LAUNCHER_SERVICE_NAME =
// "org.bbs.bundlemgr.BundleList"
// "org.bbs.bundlemgr.SimpleBundleList"
// "com.example.android.apis.ApiDemos"
"com.youku.tv.osgi.Activator$EmbeddedApiDemos"
;
public static final String DEFAULT_LAUNCHER_SERVICE_FILTER = "";
public static final String EXTRA_EMBEDED_ACTIVITY_CLASS_NAME = ".extra_embed_activity_class_name";
public static final String EXTRA_EMBEDED_BUNDLE_ID = ".EXTRA_EMBEDED_BUNDLE_ID";
public static final String EXTRA_INTENT_HAS_PROCESSED = ".EXTRA_INTENT_HAS_PROCESSED";
private static final boolean FORCE_CLOSE = true;
private static final boolean DEBUG_CREATE_VIEW = false;
private String mServiceName;
private String mServiceFilter;
private Resources mSourceMerger;
private LazyContext mLazyContext;
// private List<String> mExtendWidgetReg;
protected org.osgi.framework.Bundle mBundle;
private static ClassLoader sBundleClassLoader;
private static Map<WeakReference<org.osgi.framework.Bundle>, WeakReference<Resources>> sBundle2ResMap;
private static Map<WeakReference<org.osgi.framework.Bundle>, WeakReference<Application>> sBundle2AppMap;
private static Map<WeakReference<org.osgi.framework.Bundle>, WeakReference<ClassLoader>> sBundle2Classloader;
static {
sBundle2ResMap = new HashMap<WeakReference<org.osgi.framework.Bundle>, WeakReference<Resources>>();
sBundle2AppMap = new HashMap<WeakReference<org.osgi.framework.Bundle>, WeakReference<Application>>();
sBundle2Classloader = new HashMap<WeakReference<org.osgi.framework.Bundle>, WeakReference<ClassLoader>>();
}
// XXX why we need this???
// protected List<String> getExtendWidgetPackageReg() {
// List<String> w = new ArrayList<String>();
// w.add("com.youku.lib.widget.*");
// w.add("com.youku.tv.widget.*");
// w.add("com.youku.tv.ui.*");
// w.add("com.baseproject.volley.toolbox.*");
// w.add("com.youku.lib.focuslayer.*");
// return w;
// }
@Override
protected void attachBaseContext(Context newBase) {
mLazyContext = new LazyContext(newBase);
super.attachBaseContext(mLazyContext);
}
@Override
public void setTheme(int resid) {
super.setTheme(resid);
}
@Override
public Theme getTheme() {
return super.getTheme();
}
public Resources getResources() {
// this will call before onCreate().
// initActivityAgent();
// return super.getResources();
return mLazyContext.getResources();
// return mSourceMerger == null ? super.getResources() : mSourceMerger;
}
// life-cycle
@Override
protected void onCreate(Bundle savedInstanceState) {
// call this as early as possible.
InstrumentationWrapper.injectInstrumentation(this, this);
super.onCreate(savedInstanceState);
if (null == mActivityStub) {
TextView t = new TextView(this);
t.setText("no service avaiable: \n" + "serviceName: " + mServiceName
+ " serviceFilter: " + mServiceFilter);
setContentView(t);
if (FORCE_CLOSE) {
throw new IllegalArgumentException("no ActivityAgent avaiable.");
}
}
}
// private method.
protected ActivityAgent onPrepareActivityStub() {
ActivityAgent agent = null;
Intent intent = getIntent();
// FIXME get a correct classloader for extra.
if (null != sBundleClassLoader) {
intent.setExtrasClassLoader(sBundleClassLoader);
}
String activityClassName = intent.getStringExtra(EXTRA_EMBEDED_ACTIVITY_CLASS_NAME);
long bundleId = intent.getLongExtra(EXTRA_EMBEDED_BUNDLE_ID, -1);
Log.d(TAG, "embed activityName: " + activityClassName);
Log.d(TAG, "embed bundle id: " + bundleId);
BundleContext bundleContext = FelixWrapper.getInstance(null).getFramework().getBundleContext();
mBundle = bundleContext.getBundle(bundleId);
if (null != mBundle) {
// FIXME update theme.
WeakReference<Resources> r = sBundle2ResMap.get(mBundle);
if (null != r) {
mSourceMerger = r.get();
LazyContext.bundleReady(mLazyContext, mBundle, mSourceMerger, null);
}
if (null == mSourceMerger) {
Resources bundleRes = getBundleResources(mBundle);
if (bundleRes != null) {
mSourceMerger = new ResourcesMerger(bundleRes, super.getResources());
mLazyContext.bundleReady(mLazyContext,mBundle, mSourceMerger, null);
sBundle2ResMap.put(new WeakReference(mBundle), new WeakReference(mSourceMerger));
}
}
ClassLoader cl = mBundle.adapt(BundleWiring.class).getClassLoader();
if (null != cl) {
getIntent().setExtrasClassLoader(cl);
}
agent = new SimpleActivityAgent(mBundle, activityClassName);
} else {
ClassLoader cl = null;
Reference<ClassLoader> rc = sBundle2Classloader.get(new WeakReference(mBundle));
if (null != rc) {
cl = rc.get();
}
if (null != cl) {
intent.setExtrasClassLoader(cl);;
}
mServiceName = intent.getStringExtra(EXTRA_SERVICE_NAME);
if (TextUtils.isEmpty(mServiceName)) {
mServiceName = getDefaultLauncherServiceName();;
}
mServiceFilter = intent.getStringExtra(EXTRA_SERVICE_FILTER);
ServiceReference<?> s = null;
if (TextUtils.isEmpty(mServiceFilter)) {
s = bundleContext.getServiceReference(mServiceName);
} else {
try {
s = bundleContext.getServiceReferences(mServiceName, mServiceFilter)[0];
} catch (InvalidSyntaxException e) {
e.printStackTrace();
}
}
if (null != s) {
mBundle = s.getBundle();
if (null == cl) {
cl = mBundle.adapt(BundleWiring.class).getClassLoader();
sBundleClassLoader = cl;
if (null != cl) {
sBundle2Classloader.put(new WeakReference(mBundle), new WeakReference(cl));
}
}
Log.d(TAG, "bundle classloader: " + cl);
// FIXME update theme.
WeakReference<Resources> r = sBundle2ResMap.get(mBundle);
if (null != r) {
mSourceMerger = r.get();
mLazyContext.bundleReady(mLazyContext, mBundle, mSourceMerger, null);
}
if (null == mSourceMerger) {
Resources bundleRes = getBundleResources(s.getBundle());
if (bundleRes != null) {
mSourceMerger = new ResourcesMerger(bundleRes, super.getResources());
mLazyContext.bundleReady(mLazyContext,mBundle, mSourceMerger, null);
sBundle2ResMap.put(new WeakReference(mBundle), new WeakReference(mSourceMerger));
}
}
agent = (ActivityAgent) bundleContext.getService(s);
}
}
// update app map
if (null != agent) {
WeakReference<Application> r = sBundle2AppMap.get(mBundle);
if (null == r) {
Application app = agent.getBundleApplication();
if (null != app) {
((App)getApplication()).attachBundleAplication(new EmbeddedApplictionAgent(app), mBundle, mSourceMerger, mLazyContext);
sBundle2AppMap.put(new WeakReference(mBundle), new WeakReference(app));
}
}
}
// agent.mHostActivity = this;
// if (null != mSourceMerger) {
// agent.onBundleResourceReady(mSourceMerger);
// }
return agent;
}
protected String getDefaultLauncherServiceName() {
return DEFAULT_LAUNCHER_SERVICE_NAME;
}
// @Override
// public View onCreateView(String name, Context context, AttributeSet attrs) {
// // TODO Auto-generated method stub
//
// if (DEBUG_CREATE_VIEW) {
// ////////////1234567890123456789
// Log.d(TAG, "onCreateView. name: " + name);
// }
// return super.onCreateView(name, context, attrs);
// }
//
// @SuppressLint("NewApi")
// @Override
// public View onCreateView(View parent, String name, Context context,
// AttributeSet attrs) {
// if (mExtendWidgetReg == null){
// mExtendWidgetReg = getExtendWidgetPackageReg();
// }
//
// if (DEBUG_CREATE_VIEW) {
// ////////////1234567890123456789
// Log.d(TAG, "onCreateView. name: " + name + " parent: " + parent);
// }
//
// if (null != mExtendWidgetReg) {
// for (String r : mExtendWidgetReg) {
// if (name.matches(r)){
// try {
// if (DEBUG_CREATE_VIEW) {
// ////////////1234567890123456789012345
// Log.d(TAG, "try to load class. name: " + name);
// }
//
// Class clazz = mBundle.loadClass(name);
// Constructor c = clazz.getDeclaredConstructor(new Class[]{Context.class, AttributeSet.class});
//
// if (DEBUG_CREATE_VIEW) {
// ////////////1234567890123456789012345
// Log.d(TAG, "load class success. name: " + name);
// }
//
// return (View) c.newInstance(new Object[]{context, attrs});
// } catch (ClassNotFoundException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (NoSuchMethodException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (InstantiationException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (IllegalAccessException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (IllegalArgumentException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (InvocationTargetException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// }
// }
//
// return super.onCreateView(parent, name, context, attrs);
// }
private Resources getBundleResources(org.osgi.framework.Bundle bundle) {
File resApk = getFileStreamPath("id" + bundle.getBundleId() + "_v" + bundle.getVersion());
//debug
resApk.delete();
if (!resApk.exists()) {
URL url = bundle.getResource(".");
try {
InputStream ins = url.openStream();
OutputStream ous = new FileOutputStream(resApk);
final int LEN = 8 * 1024;
byte[] buff = new byte[LEN];
int read = -1;
while ((read = ins.read(buff)) != -1){
ous.write(buff, 0, read);
// Log.d(TAG, "" + new String(buff, 0, read));
}
ins.close();
ous.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return loadApkResource(resApk.getAbsolutePath());
}
public static Resources loadApkResource(String apkFilePath) {
AssetManager assets = null;
try {
assets = AssetManager.class.getConstructor(null).newInstance(null);
Method method = assets.getClass().getMethod("addAssetPath", new Class[]{String.class});
Object r = method.invoke(assets, apkFilePath);
DisplayMetrics metrics = null;
Configuration config = null;
Resources res = new Resources(assets, metrics, config);
return res;
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public void processIntent(Intent intent) {
Log.d(TAG, "processIntent. intent: " + intent);
if (!intent.getBooleanExtra(EXTRA_INTENT_HAS_PROCESSED, false)) {
ComponentName com = intent.getComponent();
if (null != com) {
String c = com.getClassName();
c = "com.youku.tv.osgi.Activator$Home";
if (!TextUtils.isEmpty(c)) {
intent.setComponent(new ComponentName(com.getPackageName(), EmbeddedBundleActivity.class.getCanonicalName()));
intent.putExtra(EXTRA_SERVICE_NAME, c);
intent.putExtra(EXTRA_INTENT_HAS_PROCESSED, true);
}
}
}
}
@Override
public ClassLoader getClassLoader() {
if (null != mLazyContext){
return mLazyContext.getClassLoader();
}
return super.getClassLoader();
}
@Override
public void overridePendingTransition(int enterAnim, int exitAnim) {
Log.w(TAG, "overridePendingTransition. ignore this call now.");
// FIXME
// super.overridePendingTransition(enterAnim, exitAnim);
}
}