From 3a63e20ca981931ed42d27a7ce4c6b16c9198020 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Thu, 3 May 2012 05:02:08 +0200 Subject: AndroidLauncher: Add 'MainLauncher'; ClassLoaderUtil adds list of direct APKs to add to classpath MainLauncher is capable of launching a traditional static main method from a activity. TODO: - parametrize the APK list (junit, ..) - pass-through the activity instance to be used w/ NEWT AndroidWindow --- .../jogamp/android/launcher/ActivityLauncher.java | 14 +- .../jogamp/android/launcher/ClassLoaderUtil.java | 78 ++++--- src/java/jogamp/android/launcher/LauncherMain.java | 63 ------ src/java/jogamp/android/launcher/MainLauncher.java | 239 +++++++++++++++++++++ 4 files changed, 295 insertions(+), 99 deletions(-) delete mode 100644 src/java/jogamp/android/launcher/LauncherMain.java create mode 100644 src/java/jogamp/android/launcher/MainLauncher.java (limited to 'src') diff --git a/src/java/jogamp/android/launcher/ActivityLauncher.java b/src/java/jogamp/android/launcher/ActivityLauncher.java index 7506803..bd7c723 100644 --- a/src/java/jogamp/android/launcher/ActivityLauncher.java +++ b/src/java/jogamp/android/launcher/ActivityLauncher.java @@ -34,17 +34,17 @@ import java.lang.reflect.Method; import android.app.Activity; import android.net.Uri; import android.os.Bundle; -import android.widget.TextView; import android.util.Log; public class ActivityLauncher extends Activity { static final String TAG = "JogAmp-ActivityLauncher"; - TextView tv = null; + + LauncherUtil.DataSet data = null; + + Class activityClazz = null; Method mOnCreate, mOnDestroy, mOnPause, mOnRestart, mOnResume, mOnStart, mOnStop, mSetRootActivity; - Class activityClazz = null; Object activityObject = null; - LauncherUtil.DataSet data = null; @Override public void onCreate(Bundle savedInstanceState) { @@ -55,13 +55,11 @@ public class ActivityLauncher extends Activity { data = LauncherUtil.DataSet.create(uri); data.setSystemProperties(); - ClassLoader cl = ClassLoaderUtil.createClassLoader(this, data.getPackages(), false); + ClassLoader cl = ClassLoaderUtil.createClassLoader(this, data.getPackages(), false, null); if(null != cl) { try { activityClazz = Class.forName(data.getActivityName(), true, cl); Log.d(TAG, "Activity Clazz "+activityClazz); - activityObject = createInstance(activityClazz, null); - Log.d(TAG, "Activity Object "+activityObject); mOnCreate = activityClazz.getMethod("onCreate", Bundle.class); mOnDestroy = activityClazz.getMethod("onDestroy"); mOnPause = activityClazz.getMethod("onPause"); @@ -70,6 +68,8 @@ public class ActivityLauncher extends Activity { mOnStart = activityClazz.getMethod("onStart"); mOnStop = activityClazz.getMethod("onStop"); mSetRootActivity = activityClazz.getMethod("setRootActivity", Activity.class); + activityObject = createInstance(activityClazz, null); + Log.d(TAG, "Activity Object "+activityObject); } catch (Exception e) { Log.d(TAG, "error: "+e, e); throw new RuntimeException(e); diff --git a/src/java/jogamp/android/launcher/ClassLoaderUtil.java b/src/java/jogamp/android/launcher/ClassLoaderUtil.java index 85c0b94..4e044ea 100644 --- a/src/java/jogamp/android/launcher/ClassLoaderUtil.java +++ b/src/java/jogamp/android/launcher/ClassLoaderUtil.java @@ -69,21 +69,22 @@ public class ClassLoaderUtil { } } - public static synchronized ClassLoader createClassLoader(Context ctx, List userPackageNames, boolean addLibPath) { - return createClassLoader(ctx, userPackageNames, addLibPath, null); + public static synchronized ClassLoader createClassLoader(Context ctx, List userPackageNames, boolean addUserLibPath, + List apkNames) { + return createClassLoader(ctx, userPackageNames, addUserLibPath, apkNames, null); } - public static synchronized ClassLoader createClassLoader(Context ctx, List userPackageNames, - boolean addLibPath, ClassLoader parent) { + public static synchronized ClassLoader createClassLoader(Context ctx, List userPackageNames, boolean addUserLibPath, + List apkNames, ClassLoader parent) { init(ctx); if(null==jogAmpClassLoader) { - jogAmpClassLoader = createClassLoaderImpl(ctx, Arrays.asList(packagesJogAmp), true, + jogAmpClassLoader = createClassLoaderImpl(ctx, Arrays.asList(packagesJogAmp), true, null, (null != parent ) ? parent : ctx.getClassLoader()); } parent = jogAmpClassLoader; - return createClassLoaderImpl(ctx, userPackageNames, addLibPath, jogAmpClassLoader); + return createClassLoaderImpl(ctx, userPackageNames, addUserLibPath, apkNames, jogAmpClassLoader); } /** @@ -92,8 +93,8 @@ public class ClassLoaderUtil { * @param userPackageNames list of user package names, the last entry shall reflect the Activity * @return */ - private static synchronized ClassLoader createClassLoaderImpl(Context ctx, List userPackageNames, - boolean addLibPath, ClassLoader parent) { + private static synchronized ClassLoader createClassLoaderImpl(Context ctx, List userPackageNames, boolean addUserLibPath, + List apkNames, ClassLoader parent) { final ApplicationInfo appInfo = ctx.getApplicationInfo(); @@ -106,34 +107,53 @@ public class ClassLoaderUtil { int apkCount = 0; String lastUserPackageName = null; // the very last one reflects the Activity - for(Iterator i=userPackageNames.iterator(); i.hasNext(); ) { - lastUserPackageName = i.next(); - String userAPK = null; - try { - userAPK = ctx.getPackageManager().getApplicationInfo(lastUserPackageName,0).sourceDir; - } catch (PackageManager.NameNotFoundException e) { - Log.d(TAG, "error: "+e, e); + if( null != userPackageNames ) { + for(Iterator i=userPackageNames.iterator(); i.hasNext(); ) { + lastUserPackageName = i.next(); + String userAPK = null; + try { + userAPK = ctx.getPackageManager().getApplicationInfo(lastUserPackageName,0).sourceDir; + } catch (PackageManager.NameNotFoundException e) { + Log.d(TAG, "error: "+e, e); + } + if(null != userAPK) { + if(apkCount>0) { + apks.append(ELEM_SEP); + if(addUserLibPath) { + libs.append(ELEM_SEP); + } + } + apks.append(userAPK); + if(addUserLibPath) { + libs.append(appDir).append(PATH_SEP).append(lastUserPackageName).append(PATH_SEP).append(libSub).append(PATH_SEP); + } + Log.d(TAG, "APK found: <"+lastUserPackageName+"> -> <"+userAPK+">"); + apkCount++; + } else { + Log.d(TAG, "APK not found: <"+lastUserPackageName+">"); + } + } + if( userPackageNames.size() != apkCount ) { + Log.d(TAG, "User APKs incomplete, abort (1)"); + return null; } - if(null != userAPK) { + } + final int userAPKCount = apkCount; + + if( null != apkNames ) { + for(Iterator i=apkNames.iterator(); i.hasNext(); ) { + String userAPK = i.next(); if(apkCount>0) { apks.append(ELEM_SEP); - if(addLibPath) { - libs.append(ELEM_SEP); - } } apks.append(userAPK); - if(addLibPath) { - libs.append(appDir).append(PATH_SEP).append(lastUserPackageName).append(PATH_SEP).append(libSub).append(PATH_SEP); - } - Log.d(TAG, "APK found: <"+lastUserPackageName+"> -> <"+userAPK+">"); + Log.d(TAG, "APK added: <"+userAPK+">"); apkCount++; - } else { - Log.d(TAG, "APK not found: <"+lastUserPackageName+">"); } - } - if( userPackageNames.size()!=apkCount ) { - Log.d(TAG, "APKs incomplete, abort"); - return null; + if( apkNames.size() != apkCount - userAPKCount ) { + Log.d(TAG, "Framework APKs incomplete, abort (2)"); + return null; + } } // return new TraceDexClassLoader(apks.toString(), dexPath.getAbsolutePath(), libs.toString(), parent); diff --git a/src/java/jogamp/android/launcher/LauncherMain.java b/src/java/jogamp/android/launcher/LauncherMain.java deleted file mode 100644 index eb56385..0000000 --- a/src/java/jogamp/android/launcher/LauncherMain.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2011 JogAmp Community. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * The views and conclusions contained in the software and documentation are those of the - * authors and should not be interpreted as representing official policies, either expressed - * or implied, of JogAmp Community. - */ -package jogamp.android.launcher; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import android.app.ActivityGroup; -import android.content.Intent; -import android.os.Looper; -import android.util.Log; - -public class LauncherMain { - static final String TAG = "NEWTLauncherMain"; - - static String userActivityName = null; - static ArrayList userPackageNames = new ArrayList(); - static boolean setAPKClasspath = false; - - public static String getUserActivityName() { return userActivityName; } - public static List getUserPackageNames() { return userPackageNames; } - - public static void main(String args[]) throws IOException, ClassNotFoundException { - userActivityName = "com.jogamp.opengl.test.android.NEWTGearsES2Activity"; - userPackageNames.add("com.jogamp.opengl.test"); - Looper.prepareMainLooper(); - ActivityGroup activityGroup = new ActivityGroup(true); - ClassLoader cl = ClassLoaderUtil.createClassLoader(activityGroup, getUserPackageNames(), false); - if(null != cl) { - Class activityClazz = Class.forName(getUserActivityName(), true, cl); - Intent intent = new Intent(activityGroup, activityClazz); - Log.d(TAG, "Launching Activity: "+activityClazz+", "+intent); - android.view.Window activityWindow = activityGroup.getLocalActivityManager().startActivity ("ID001", intent); - // activityGroup.addContentView(view, params) - } - } -} diff --git a/src/java/jogamp/android/launcher/MainLauncher.java b/src/java/jogamp/android/launcher/MainLauncher.java new file mode 100644 index 0000000..12dafb7 --- /dev/null +++ b/src/java/jogamp/android/launcher/MainLauncher.java @@ -0,0 +1,239 @@ +/** + * Copyright 2011 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.android.launcher; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +import android.app.Activity; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; + +public class MainLauncher extends Activity { + static final String TAG = "JogAmp-MainLauncher"; + + // private static final String[] frameworkAPKs = { "/system/framework/core-junit.jar", "/data/projects/gluegen/make/lib/ant-junit-all.apk" }; + private static final String[] frameworkAPKs = { "/data/projects/gluegen/make/lib/ant-junit-all.apk" }; + + LauncherUtil.DataSet data = null; + + Class mainClazz = null; + Method mainClazzMain = null; + + Class staticContextClazz = null; + Method mStaticContextInit = null; + Method mStaticContextClear = null; + String[] mainClassArgs = null; + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.d(TAG, "onCreate - S"); + super.onCreate(savedInstanceState); + + final Uri uri = getIntent().getData(); + data = LauncherUtil.DataSet.create(uri); + data.setSystemProperties(); + + ClassLoader cl = ClassLoaderUtil.createClassLoader(this, data.getPackages(), false, Arrays.asList(frameworkAPKs)); + if(null != cl) { + try { + staticContextClazz = Class.forName("jogamp.common.os.android.StaticContext", true, cl); + mStaticContextInit = staticContextClazz.getMethod("init", android.content.Context.class); + mStaticContextClear = staticContextClazz.getMethod("clear"); + + mainClazz = Class.forName(data.getActivityName(), true, cl); + Log.d(TAG, "Main Clazz "+mainClazz); + mainClazzMain = mainClazz.getDeclaredMethod("main", new Class[] { String[].class }); + Log.d(TAG, "Main Clazz Main "+mainClazzMain); + } catch (Exception e) { + Log.d(TAG, "error: "+e, e); + throw new RuntimeException(e); + } + } + + if( null == mStaticContextInit || null == mStaticContextClear || + null == mainClazzMain ) { + RuntimeException e = new RuntimeException("XXX - incomplete method set"); + Log.d(TAG, "error: "+e, e); + throw e; + } + + callMethod(null, mStaticContextInit, this.getApplicationContext()); + + mainClassArgs=new String[0]; // FIXME + + Log.d(TAG, "onCreate - X"); + } + + @Override + public void onStart() { + Log.d(TAG, "onStart - S"); + super.onStart(); + Log.d(TAG, "onStart - X"); + } + + @Override + public void onRestart() { + Log.d(TAG, "onRestart - S"); + super.onRestart(); + Log.d(TAG, "onRestart - X"); + } + + @Override + public void onResume() { + Log.d(TAG, "onResume - S"); + try { + mainClazzMain.invoke(null, new Object[] { mainClassArgs } ); + } catch (InvocationTargetException ite) { + ite.getTargetException().printStackTrace(); + } catch (Throwable t) { + t.printStackTrace(); + } + super.onResume(); + finish(); + Log.d(TAG, "onResume - X"); + } + + @Override + public void onPause() { + Log.d(TAG, "onPause - S"); + super.onPause(); + Log.d(TAG, "onPause - X"); + } + + @Override + public void onStop() { + Log.d(TAG, "onStop - S"); + super.onStop(); + Log.d(TAG, "onStop - X"); + } + + @Override + public void onDestroy() { + Log.d(TAG, "onDestroy - S"); + if(null != data) { + data.clearSystemProperties(); + data = null; + } + callMethod(null, mStaticContextClear); + super.onDestroy(); + finish(); + Log.d(TAG, "onDestroy - X"); + } + + @Override + public void finish() { + Log.d(TAG, "finish - S"); + super.finish(); + Log.d(TAG, "finish - X"); + } + + /** + * @throws JogampRuntimeException if the instance can not be created. + */ + public static final Object createInstance(Class clazz, Class[] cstrArgTypes, Object ... cstrArgs) + throws RuntimeException + { + return createInstance(getConstructor(clazz, cstrArgTypes), cstrArgs); + } + + public static final Object createInstance(Constructor cstr, Object ... cstrArgs) + throws RuntimeException + { + try { + return cstr.newInstance(cstrArgs); + } catch (Exception e) { + Throwable t = e; + if (t instanceof InvocationTargetException) { + t = ((InvocationTargetException) t).getTargetException(); + } + if (t instanceof Error) { + throw (Error) t; + } + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + throw new RuntimeException("can not create instance of "+cstr.getName(), t); + } + } + + /** + * @throws JogampRuntimeException if the constructor can not be delivered. + */ + protected static final Constructor getConstructor(Class clazz, Class ... cstrArgTypes) + throws RuntimeException { + try { + if(null == cstrArgTypes) { + cstrArgTypes = zeroTypes; + } + return clazz.getDeclaredConstructor(cstrArgTypes); + } catch (NoSuchMethodException ex) { + throw new RuntimeException("Constructor: '" + clazz + "(" + asString(cstrArgTypes) + ")' not found", ex); + } + } + + protected static final Class[] zeroTypes = new Class[0]; + + protected static final String asString(Class[] argTypes) { + StringBuilder args = new StringBuilder(); + boolean coma = false; + if(null != argTypes) { + for (int i = 0; i < argTypes.length; i++) { + if(coma) { + args.append(", "); + } + args.append(argTypes[i].getName()); + coma = true; + } + } + return args.toString(); + } + + protected static final Object callMethod(Object instance, Method method, Object ... args) + throws RuntimeException + { + try { + return method.invoke(instance, args); + } catch (Exception e) { + Throwable t = e; + if (t instanceof InvocationTargetException) { + t = ((InvocationTargetException) t).getTargetException(); + } + if (t instanceof Error) { + throw (Error) t; + } + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + throw new RuntimeException("calling "+method+" failed", t); + } + } +} -- cgit v1.2.3