package org.bbs.apklauncher; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; import org.bbs.felix.util.PackageParser.PackageInfoX.ActivityInfoX; import org.bbs.osgi.activity.AbsActivityWraper; import org.bbs.osgi.activity.BundleActivity; import org.bbs.osgi.activity.InstrumentationWrapper; import org.bbs.osgi.activity.InstrumentationWrapper.CallBack; import org.bbs.osgi.activity.LazyContext; import org.bbs.osgi.activity.ReflectUtil; import org.bbs.osgi.activity.ResourcesMerger; import org.bbs.osgi.activity.embed.EmbeddedActivityAgent; import android.app.Activity; import android.app.Application; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.os.Bundle; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.View; import dalvik.system.DexClassLoader; public class StubActivity extends AbsActivityWraper implements CallBack { /** * type {@link String} */ public static final String EXTRA_ACTIVITY_CLASS_NAME = "EXTRA_ACTIVITY_CLASS_NAME"; /** * type {@link String} */ public static final String EXTRA_LIB_PATH = "EXTRA_LIB_PATH"; static final String TAG = StubActivity.class.getSimpleName(); public static Map<String, WeakReference<ClassLoader>> sApk2ClassLoaderMap = new HashMap<String, WeakReference<ClassLoader>>(); public static Map<String, WeakReference<ResourcesMerger>> sApk2ResourceMap = new HashMap<String, WeakReference<ResourcesMerger>>(); public static Map<String, WeakReference<Context>> sApk2ContextMap = new HashMap<String, WeakReference<Context>>(); public static Map<String, WeakReference<Application>> sApk2ApplicationtMap = new HashMap<String, WeakReference<Application>>(); private ClassLoader mClassLoader; private String mApplicationClassName; private String mActivityClassName; private String mApkPath; private Activity mTargetActivity; private ResourcesMerger mResourceMerger; private LazyContext mTargetContext; private int mTargetThemeId; private Theme mTargetTheme; private ActivityInfoX mActInfo; private static ClassLoader sLastClassLoader; // private int mThemeResource = android.R.style.Theme_Black; private Theme mTheme; private PackageManager mPackageManager; private Resources mTargetResource; private String mLibPath; private PackageManager mSysPm; private Context mRealBaseContext; private String mPackageName; @Override protected void onCreate(Bundle savedInstanceState) { // mThemeResource = (Integer) ReflectUtil.getFiledValue(ContextThemeWrapper.class, this, "mThemeResource"); // getThemeResId(); super.onCreate(savedInstanceState); updateTitle(); } private ClassLoader onCreateClassLoader(String apkPath, String libPath) { ClassLoader c = new DexClassLoader(apkPath, getDir("apk_code_cache", 0).getPath(), libPath, mRealBaseContext.getClassLoader()); Log.d(TAG, "new classloader for apk: " + c); return c; } @Override protected void attachBaseContext(Context newBase) { mRealBaseContext = newBase; // super.attachBaseContext(newBase); mTargetContext = new LazyContext(newBase); super.attachBaseContext(mTargetContext); mSysPm = getPackageManager(); } protected Activity onPrepareActivityStub() { Intent intent = getIntent(); // how to get classloader berfore parse intent. if (sLastClassLoader != null) { mTargetContext.classLoaderReady(sLastClassLoader); intent.setExtrasClassLoader(sLastClassLoader); } mActivityClassName = intent.getStringExtra(EXTRA_ACTIVITY_CLASS_NAME); mLibPath = intent.getStringExtra(EXTRA_LIB_PATH); mActInfo = InstalledAPks.getInstance().getActivityInfo(mActivityClassName); mApplicationClassName = mActInfo.applicationInfo.className; mTargetThemeId = mActInfo.theme; mApkPath = mActInfo.mApkPath; if (TextUtils.isEmpty(mApplicationClassName)){ mApplicationClassName = Application.class.getCanonicalName(); Log.d(TAG, "no packageName, user default."); } mPackageName = mActInfo.packageName; Log.d(TAG, "mApplicationClassName: " + mApplicationClassName); Log.d(TAG, "mPackageName : " + mPackageName); Log.d(TAG, "mActivityClassName : " + mActivityClassName); Log.d(TAG, "mThemeId : " + mTargetThemeId); Log.d(TAG, "mApkPath : " + mApkPath); Log.d(TAG, "mLibPath : " + mLibPath); WeakReference<ClassLoader> r = sApk2ClassLoaderMap.get(mApkPath); if (r != null && r.get() != null) { mClassLoader = r.get(); } else { mClassLoader = onCreateClassLoader(mApkPath, mLibPath); sApk2ClassLoaderMap.put(mApkPath, new WeakReference<ClassLoader>(mClassLoader)); } sLastClassLoader = mClassLoader; mTargetContext.classLoaderReady(mClassLoader); mTargetContext.packageManagerReady(new PakcageMangerPolicy(mSysPm)); mTargetContext.packageNameReady(mActInfo.packageName); // do appliction init. must before activity init. Application app = null; WeakReference<Application> rp = sApk2ApplicationtMap.get(mApkPath); if (rp != null && rp.get() != null) { app = rp.get(); } else { if (!TextUtils.isEmpty(mApplicationClassName)) { try { app = ((Application) mClassLoader.loadClass(mApplicationClassName).newInstance()); sApk2ApplicationtMap.put(mApkPath, new WeakReference<Application>(app)); LazyContext appBaseContext = new LazyContext(getApplication()); appBaseContext.applicationReady(app); Resources appRes = BundleActivity.loadApkResource(mApkPath); appRes = new ResourcesMerger(appRes, getResources()); appBaseContext.resReady(appRes); int appTheme = mActInfo.applicationInfo.theme; if (appTheme > 0) { } else { } appTheme = ReflectUtil.ResourceUtil.selectDefaultTheme(appRes, appTheme, mActInfo.applicationInfo.targetSdkVersion); Log.d(TAG, "resolved application theme: " + appTheme); appBaseContext.themeReady(appTheme); appBaseContext.packageManagerReady(new PakcageMangerPolicy(mSysPm)); appBaseContext.packageNameReady(mPackageName); ((ApkLauncherApplication)getApplication()).attachBundleAplication(app, appBaseContext); sApk2ApplicationtMap.put(mApkPath, new WeakReference<Application>(app)); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("error in create application: " + mApplicationClassName , e); } } } // do activity init InstrumentationWrapper.injectInstrumentation(this, this); try { mTargetActivity = (Activity) mClassLoader.loadClass(mActivityClassName).newInstance(); dumpActivityType(mTargetActivity); WeakReference<ResourcesMerger> rr = sApk2ResourceMap.get(mApkPath); if (rr != null && rr.get() != null) { mResourceMerger = rr.get(); mTargetResource = mResourceMerger.mFirst; } else { mTargetResource = BundleActivity.loadApkResource(mApkPath); mResourceMerger = new ResourcesMerger(mTargetResource, getResources()); sApk2ResourceMap.put(mApkPath, new WeakReference<ResourcesMerger>(mResourceMerger)); } if (mTargetThemeId > 0) { } else { } mTargetThemeId = ReflectUtil.ResourceUtil.selectDefaultTheme(mResourceMerger, mTargetThemeId, mActInfo.applicationInfo.targetSdkVersion); Log.d(TAG, "resolved activity theme: " + mTargetThemeId); mTargetContext.setTheme(mTargetThemeId); mTargetContext.themeReady(mTargetThemeId); mTargetContext.packageManagerReady(new PakcageMangerPolicy(mSysPm)); mTargetContext.packageNameReady(mPackageName); mTargetContext.resReady(mResourceMerger); EmbeddedActivityAgent.copyContext(this, mTargetActivity, mResourceMerger); if (null == app) { throw new IllegalStateException("target apk app is null."); } ReflectUtil.ActivityReflectUtil.setApplication(mTargetActivity, app); ReflectUtil.ActivityReflectUtil.setResource(this, mResourceMerger); ReflectUtil.ActivityReflectUtil.setResource(mTargetActivity, mResourceMerger); ReflectUtil.ActivityReflectUtil.setBaseContext(mTargetActivity, mTargetContext); // ReflectUtil.ActivityReflectUtil.setWindowContext(getWindow(), mTargetContext); mTargetActivity.setTheme(mTargetThemeId); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("error in create activity: " + mActivityClassName , e); } return mTargetActivity; } private void dumpActivityType(Activity activity) { Class clazz = activity.getClass(); //==========+++++++++++ Log.d(TAG, "class : " + clazz + " name: " + clazz.getName()); while (!clazz.getName().equals(Object.class.getName())) { clazz = clazz.getSuperclass(); //==========+++++++++++ Log.d(TAG, "super class: " + clazz); } } private void updateTitle() { CharSequence title = ""; if (mActInfo.labelRes > 0) { title = mResourceMerger.getString(mActInfo.labelRes); } if (TextUtils.isEmpty(title)) { title = mActInfo.nonLocalizedLabel; } if (!TextUtils.isEmpty(title)) { //mTargetActivity.onCreate() is not called. try { mTargetActivity.setTitle(title); } catch (Exception e) { e.printStackTrace(); } setTitle(title); } } @Override public Theme getTheme() { return mTargetContext.getTheme(); } // for Window to get target's resource public Resources getResources() { return mTargetContext.getResources(); } @Override public void processIntent(Intent intent) { Log.d(TAG, "processIntent. intent: " + intent); ComponentName com = intent.getComponent(); if (null != com) { String c = com.getClassName(); intent.putExtra(EXTRA_ACTIVITY_CLASS_NAME, c); if (!TextUtils.isEmpty(c)) { intent.setComponent(new ComponentName(mRealBaseContext.getPackageName(), StubActivity.class.getCanonicalName())); intent.putExtra(StubActivity.EXTRA_LIB_PATH, mLibPath); ActivityInfoX a = InstalledAPks.getInstance().getActivityInfo(c); if (a != null) { ApkLuncherActivity.putExtra(a, intent); } } } else { ResolveInfo a = getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); Log.d(TAG, "ResolveInfo a: " + a); } } @Override public View onCreateView(String name, Context context, AttributeSet attrs) { // Log.d(TAG, "onCreateView(). name: " + name); return super.onCreateView(name, context, attrs); } }