-
Enhancement
-
Resolution: Fixed
-
P4
-
8u20
some of the dalvik-specific classes have been updated in the bitbucket repository, and the changes are not reflected in openJFX.
Below are the changes for the Launcher and Activity classes, and a new class DalvikInput
diff -r 1ed6990e4ff3 modules/graphics/src/dalvik/java/javafxports/android/DalvikLauncher.java
--- a/modules/graphics/src/dalvik/java/javafxports/android/DalvikLauncher.java Wed Jun 04 09:32:03 2014 +0200
+++ b/modules/graphics/src/dalvik/java/javafxports/android/DalvikLauncher.java Thu Jun 05 14:46:16 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,16 +27,18 @@
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Bundle;
+import android.content.res.AssetManager;
import android.util.Log;
import dalvik.system.DexClassLoader;
+import dalvik.system.PathClassLoader;
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Map.Entry;
import java.util.Properties;
-import java.lang.ClassLoader;
public class DalvikLauncher implements Launcher {
@@ -45,8 +47,6 @@
private static final String COM_SUN_JAVAFX_APPLICATION_LAUNCHERIMPL = "com.sun.javafx.application.LauncherImpl";
private static final String LAUNCH_APPLICATION_METHOD = "launchApplication";
private static final String MAIN_METHOD = "main";
- private static final String META_DATA_MAIN_CLASS = "main.class";
- private static final String META_DATA_PRELOADER_CLASS = "preloader.class";
private static final String ANDROID_PROPERTY_PREFIX = "android.";
private static final Class[] LAUNCH_APPLICATION_ARGS = new Class[]{
@@ -59,14 +59,18 @@
private static boolean fxApplicationLaunching = false;
private Activity activity;
- private Bundle metadata;
+ private String preloaderClassName, mainClassName;
- public void launchApp(Activity a, Bundle metadata) {
+ public void launchApp(Activity a, String mainClassName, String preloaderClassName) {
this.activity = a;
- this.metadata = metadata;
+ this.preloaderClassName = preloaderClassName;
+ this.mainClassName = mainClassName;
+
+ InputStream is = null;
Properties userProperties = new Properties();
try {
- userProperties.load(DalvikLauncher.class.getResourceAsStream("/javafx.platform.properties"));
+ is = new BufferedInputStream(this.activity.getAssets().open("javafx.platform.properties"));
+ userProperties.load(is);
String key = null;
for (Entry<Object, Object> e : userProperties.entrySet()) {
key = (String) e.getKey();
@@ -78,14 +82,23 @@
} catch (IOException e) {
throw new RuntimeException("Can't load properties", e);
+ } finally {
+ try { is.close(); } catch(Exception e) {}
+
}
Log.v(TAG, "Launch JavaFX application on dalvik vm.");
try {
+ initMethodHandles();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to init method handles", e);
+ }
+
+ try {
final Class applicationClass = resolveApplicationClass();
final Class preloaderClass = resolvePreloaderClass();
- final Class javafxApplicationClass = Class.forName(JAVAFX_APPLICATION_APPLICATION);
- final Class javafxLauncherClass = Class.forName(COM_SUN_JAVAFX_APPLICATION_LAUNCHERIMPL);
+ final Class javafxApplicationClass = getApplicationClassLoader().loadClass(JAVAFX_APPLICATION_APPLICATION);
+ final Class javafxLauncherClass = getApplicationClassLoader().loadClass(COM_SUN_JAVAFX_APPLICATION_LAUNCHERIMPL);
final Method launchMethod = javafxLauncherClass.getMethod(
LAUNCH_APPLICATION_METHOD, LAUNCH_APPLICATION_ARGS);
@@ -127,42 +140,61 @@
Log.e(TAG, "Launch failed with exception.", e);
}
}
+
+ private static ClassLoader applicationClassLoader;
+
+ private ClassLoader getApplicationClassLoader() {
+ if (applicationClassLoader == null) {
+ // Internal storage where the DexClassLoader writes the optimized dex file to.
+ final File optimizedDexOutputPath = activity.getDir("outdex", Context.MODE_PRIVATE);
+ // Initialize the class loader with the secondary dex file.
+ // This includes the javafx, compatibility and application classes
+
+ ClassLoader cl = new DexClassLoader(FXActivity.dexClassPath,
+ optimizedDexOutputPath.getAbsolutePath(),
+ FXActivity.getInstance().getApplicationInfo().nativeLibraryDir,
+ activity.getClassLoader());
+ Thread.currentThread().setContextClassLoader(cl);
+ applicationClassLoader = cl;
+ Log.v(TAG, "Application classloader initialized: " + applicationClassLoader);
+ }
+ return applicationClassLoader;
+ }
+
+ private void initMethodHandles() throws ClassNotFoundException, NoSuchMethodException, SecurityException {
+ Class<?> dalvikInputClass = getApplicationClassLoader().loadClass("com.sun.glass.ui.android.DalvikInput");
+ FXActivity.getInstance().setOnMultiTouchEventMethod(dalvikInputClass.getMethod("onMultiTouchEvent", int.class, int[].class, int[].class, int[].class, int[].class));
+ FXActivity.getInstance().setOnKeyEventMethod(dalvikInputClass.getMethod("onKeyEvent", int.class, int.class, String.class));
+ FXActivity.getInstance().setOnSurfaceChangedNativeMethod1(dalvikInputClass.getMethod("onSurfaceChangedNative"));
+ FXActivity.getInstance().setOnSurfaceChangedNativeMethod2(dalvikInputClass.getMethod("onSurfaceChangedNative", int.class, int.class, int.class));
+ FXActivity.getInstance().setOnSurfaceRedrawNeededNativeMethod(dalvikInputClass.getMethod("onSurfaceRedrawNeededNative"));
+ FXActivity.getInstance().setOnConfigurationChangedNativeMethod(dalvikInputClass.getMethod("onConfigurationChangedNative", int.class));
+ }
+
private Class resolveApplicationClass()
throws PackageManager.NameNotFoundException, ClassNotFoundException {
- // Internal storage where the DexClassLoader writes the optimized dex file to.
- final File optimizedDexOutputPath = activity.getDir("outdex", Context.MODE_PRIVATE);
- final File dexInternalStoragePath = new File(activity.getDir("dex", Context.MODE_PRIVATE),
- FXActivity.APPLICATION_DEX_NAME);
-
- ClassLoader cl = null;
- if (dexInternalStoragePath.exists()) {
- // Initialize the class loader with the secondary dex file.
- cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
- optimizedDexOutputPath.getAbsolutePath(),
- null,
- activity.getClassLoader());
- } else {
- cl = activity.getClassLoader();
- }
+ ClassLoader cl = getApplicationClassLoader();
Class clazz = null;
-
- String applicationClassName = metadata.getString(META_DATA_MAIN_CLASS);
- if (applicationClassName != null && applicationClassName.length() > 0) {
- clazz = cl.loadClass(applicationClassName);
+ if (mainClassName != null && mainClassName.length() > 0) {
+ clazz = cl.loadClass(mainClassName);
}
+ // we set the contextClassLoader of the current thread to the one that loads the
+ // application code. Doing so, the FXMLLoader can resolve application classes
+ Thread.currentThread().setContextClassLoader(cl);
return clazz;
}
private Class resolvePreloaderClass()
throws PackageManager.NameNotFoundException, ClassNotFoundException {
+ ClassLoader cl = getApplicationClassLoader();
+
Class clazz = null;
- String className = metadata.getString(META_DATA_PRELOADER_CLASS);
- if (className != null && className.length() > 0) {
- clazz = Class.forName(className);
+ if (preloaderClassName != null && preloaderClassName.length() > 0) {
+ clazz = cl.loadClass(preloaderClassName);
}
return clazz;
}
diff -r 1ed6990e4ff3 modules/graphics/src/dalvik/java/javafxports/android/FXActivity.java
--- a/modules/graphics/src/dalvik/java/javafxports/android/FXActivity.java Wed Jun 04 09:32:03 2014 +0200
+++ b/modules/graphics/src/dalvik/java/javafxports/android/FXActivity.java Thu Jun 05 14:46:16 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,11 +22,13 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package javafxports.android;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
@@ -46,14 +48,21 @@
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.util.Properties;
import java.util.concurrent.CountDownLatch;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
-import javafx.application.Platform;
+
public class FXActivity extends Activity implements SurfaceHolder.Callback,
SurfaceHolder.Callback2 {
@@ -63,10 +72,16 @@
private static final String ACTIVITY_LIB = "activity";
private static final String META_DATA_LAUNCHER_CLASS = "launcher.class";
private static final String DEFAULT_LAUNCHER_CLASS = "javafxports.android.DalvikLauncher";
+ private static final String META_DATA_MAIN_CLASS = "main.class";
+ private static final String META_DATA_PRELOADER_CLASS = "preloader.class";
private static final String META_DATA_DEBUG_PORT = "debug.port";
- public static final String APPLICATION_DEX_NAME = "Application_dex.jar";
- static final int BUF_SIZE = 8 * 1024;
+ private static final String APPLICATION_DEX_NAME = "Application_dex.jar";
+ private static final String APPLICATION_RESOURCES_NAME = "Application_resources.jar";
+ private static final String CLASSLOADER_PROPERTIES_NAME = "classloader.properties";
+ private static final String BUILD_TIME_NAME = "buildtime";
+ private static final int BUF_SIZE = 8 * 1024;
+ public static String dexClassPath = new String();
private static FXActivity instance;
private static Launcher launcher;
@@ -81,18 +96,44 @@
private static CountDownLatch cdlEvLoopFinished;
+ // Cache method handles
+ // Can not access com.sun.glass.ui.android.DalvikInput directly, because the javafx classes are loaded with a different classloader
+ private Method onMultiTouchEventMethod;
+ private Method onKeyEventMethod;
+ private Method onSurfaceChangedNativeMethod1;
+ private Method onSurfaceChangedNativeMethod2;
+ private Method onSurfaceRedrawNeededNativeMethod;
+ private Method onConfigurationChangedNativeMethod;
+
//configurations
private int SCREEN_ORIENTATION = 1;
+
+ private String launcherClassName;
+ private String mainClassName;
+ private String preloaderClassName;
+
+ private String currentBuildStamp;
+ private Properties classLoaderProperties;
+ private File dexInternalStoragePath;
+
+ private static final Bundle metadata = new Bundle();
static {
System.loadLibrary(ACTIVITY_LIB);
+ System.setProperty("javax.xml.stream.XMLInputFactory",
+ "com.sun.xml.stream.ZephyrParserFactory");
+ System.setProperty("javax.xml.stream.XMLOutputFactory",
+ "com.sun.xml.stream.ZephyrWriterFactory");
+ System.setProperty("javax.xml.stream.XMLEventFactory",
+ "com.sun.xml.stream.events.ZephyrEventFactory");
+
}
-
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Log.v(TAG, "onCreate");
+ Log.v(TAG, "onCreate FXActivity");
if (launcher != null) {
Log.v(TAG, "JavaFX application is already running");
return;
@@ -114,34 +155,22 @@
// Before the secondary dex file can be processed by the DexClassLoader,
// it has to be first copied from asset resource to a storage location.
- File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE), APPLICATION_DEX_NAME);
- BufferedInputStream bis = null;
- OutputStream dexWriter = null;
-
+ dexInternalStoragePath = getDir("dex", Context.MODE_PRIVATE);
+
try {
- bis = new BufferedInputStream(getAssets().open(APPLICATION_DEX_NAME));
- Log.v(TAG, "copy secondary dex file '" + APPLICATION_DEX_NAME + "' from asset resource to storage location");
- dexWriter = new BufferedOutputStream(new FileOutputStream(dexInternalStoragePath));
- byte[] buf = new byte[BUF_SIZE];
- int len;
- while ((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
- dexWriter.write(buf, 0, len);
+ if(isUptodate()) {
+ dexClassPath = (String) getClassLoaderProperties().get("classpath");
+ } else {
+ extractDexFiles(APPLICATION_DEX_NAME);
+ copy(APPLICATION_RESOURCES_NAME);
+ writeClassLoaderProperties();
}
} catch (FileNotFoundException e) {
- Log.v(TAG, "no secondary dex file '" + APPLICATION_DEX_NAME + "' found");
+ Log.v(TAG, "Not found application dex and resources jars.", e);
} catch (IOException e) {
- throw new RuntimeException("Failed to copy secondary dex file.", e);
- } finally {
- try {
- if (dexWriter != null) {
- dexWriter.close();
- }
- if (bis != null) {
- bis.close();
- }
- } catch (Exception e2) {
- }
+ throw new RuntimeException("Failed to copy a file.", e);
}
+ Log.v(TAG, "Secondary dex file classpath " + dexClassPath);
configuration = new DeviceConfiguration();
configuration.setConfiguration(getResources().getConfiguration());
Log.v(TAG, String.format("Confiuration orientation: %s",
@@ -154,32 +183,207 @@
jfxEventsLoop();
}
- private Bundle getMetadata() {
+ private boolean isUptodate() {
+
try {
+ String currentBuildTime = getCurrentBuildStamp();
+ if (currentBuildTime == null) {
+ return false;
+ }
+ Properties props = getClassLoaderProperties();
+ if(props == null) {
+ return false;
+ }
+ String extractedBuildTime = props.getProperty("buildStamp");
+ return currentBuildTime.equals(extractedBuildTime);
+ } catch (IOException e) {
+ Log.d(TAG, "Failed to compare build timestamps. reason:" + e.getMessage());
+ }
+ return false;
+ }
+
+ private String getCurrentBuildStamp() {
+ if(currentBuildStamp == null) {
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(getAssets().open(BUILD_TIME_NAME), "UTF-8"));
+ currentBuildStamp = reader.readLine();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to read build timestamp.", e);
+ } finally {
+ if(reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) { }
+ }
+ }
+ }
+ return currentBuildStamp;
+ }
+
+ private Properties getClassLoaderProperties() throws IOException {
+ if(classLoaderProperties == null) {
+ BufferedReader reader = null;
+ Properties props;
+ try {
+ props = new Properties();
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(dexInternalStoragePath, CLASSLOADER_PROPERTIES_NAME)), "UTF-8"));
+ props.load(reader);
+ } catch (FileNotFoundException e) {
+ return null;
+ } finally {
+ if(reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) { }
+ }
+ }
+ classLoaderProperties = props;
+ }
+ return classLoaderProperties;
+ }
+
+ private void extractDexFiles(String file) throws IOException {
+ BufferedInputStream bis = null;
+ ZipInputStream zis = null;
+ try {
+ Log.v(TAG, "extracting secondary dex files from '" + file + "' from asset resource to storage location");
+ bis = new BufferedInputStream(getAssets().open(file));
+ zis = new ZipInputStream(bis);
+ ZipEntry ze;
+ while ((ze = zis.getNextEntry()) != null) {
+ if(ze.getName().endsWith(".dex")) {
+ OutputStream dexWriter = null;
+ try {
+ File dexFile = new File(dexInternalStoragePath, ze.getName());
+ if(! dexClassPath.isEmpty()) {
+ dexClassPath += File.pathSeparator;
+ }
+ dexClassPath += dexFile.getAbsolutePath();
+ dexWriter = new BufferedOutputStream(new FileOutputStream(dexFile));
+ byte[] buf = new byte[BUF_SIZE];
+ int len;
+ while ((len = zis.read(buf, 0, BUF_SIZE)) > 0) {
+ dexWriter.write(buf, 0, len);
+ }
+ } finally {
+ if(dexWriter != null) { dexWriter.close(); };
+ }
+ }
+ }
+ } finally {
+ try {
+ if (zis != null) {
+ zis.close();
+ }
+ if (bis != null) {
+ bis.close();
+ }
+ } catch (Exception e2) {
+ }
+ }
+ }
+
+
+ private void copy(String file) throws IOException, FileNotFoundException {
+ BufferedOutputStream writer = null;
+ BufferedInputStream bis = null;
+ Log.v(TAG, "copy secondary resource file '" + file +
+ "' from asset resource to storage location");
+ bis = new BufferedInputStream(getAssets().open(file));
+ try {
+ File dexFile = new File(dexInternalStoragePath, file);
+ if(! dexClassPath.isEmpty()) {
+ dexClassPath += File.pathSeparator;
+ }
+ dexClassPath += dexFile.getAbsolutePath();
+ writer = new BufferedOutputStream(new FileOutputStream(dexFile));
+ byte[] buf = new byte[BUF_SIZE];
+ int len;
+ while ((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
+ writer.write(buf, 0, len);
+ }
+
+ } finally {
+ if(writer != null) { writer.close(); }
+ if (bis != null) {bis.close();}
+ }
+ }
+
+ private void writeClassLoaderProperties() {
+ Properties props = new Properties();
+ props.put("classpath", dexClassPath);
+ props.put("buildStamp", getCurrentBuildStamp());
+
+ BufferedOutputStream writer = null;
+ File propertyFile = new File(dexInternalStoragePath, CLASSLOADER_PROPERTIES_NAME);
+ Log.d(TAG, "writing " + propertyFile.getAbsolutePath());
+
+ try {
+ writer = new BufferedOutputStream(new FileOutputStream(propertyFile));
+ props.store(writer, null);
+ } catch (IOException e) {
+ Log.e(TAG, "failed to write " + propertyFile.getAbsolutePath(), e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) { }
+ }
+ }
+ }
+
+ private void getLauncherAndLaunchApplication() {
+ //load metadata
+ try {
+ ApplicationInfo appi = getPackageManager().getApplicationInfo(
+ getPackageName(), PackageManager.GET_META_DATA);
+ if (appi != null && appi.metaData != null) {
+ metadata.putAll(appi.metaData);
+ }
+
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Error getting Application info.");
+ }
+
+ try {
ActivityInfo ai = FXActivity.this.getPackageManager().getActivityInfo(
getIntent().getComponent(), PackageManager.GET_META_DATA);
- return ai.metaData;
+ if (ai != null && ai.metaData != null) {
+ metadata.putAll(ai.metaData);
+ }
} catch (NameNotFoundException e) {
- throw new RuntimeException("Error getting activity info", e);
+ Log.w(TAG, "Error getting Activity info.");
}
- }
-
- private void getLauncherAndLaunchApplication() {
- Bundle metaData = getMetadata();
- int dport = metaData.getInt(META_DATA_DEBUG_PORT);
+
+ int dport = metadata.getInt(META_DATA_DEBUG_PORT);
if (dport > 0) {
android.os.Debug.waitForDebugger();
}
- String launcherClass = metaData.getString(
- META_DATA_LAUNCHER_CLASS);
- if (launcherClass == null) {
- launcherClass = DEFAULT_LAUNCHER_CLASS;
+
+ launcherClassName = metadata.containsKey(META_DATA_LAUNCHER_CLASS) ?
+ metadata.getString(META_DATA_LAUNCHER_CLASS) : DEFAULT_LAUNCHER_CLASS;
+
+ mainClassName = metadata.containsKey(META_DATA_MAIN_CLASS) ?
+ metadata.getString(META_DATA_MAIN_CLASS) : null;
+
+ preloaderClassName = metadata.containsKey(META_DATA_PRELOADER_CLASS) ?
+ metadata.getString(META_DATA_PRELOADER_CLASS) : null;
+ if (mainClassName == null || mainClassName.length() == 0) {
+ throw new RuntimeException("Main application class must be defined.\n"
+ + "Use <meta-data android.name=\"main.class\" "
+ + "android.value=\"your.package.YourMainClass\"/>");
}
+ if (preloaderClassName != null && preloaderClassName.length() == 0) {
+ preloaderClassName = null;
+ }
+
+ //launch application
try {
- Class clazz = Class.forName(launcherClass);
- launcher = (Launcher) clazz.newInstance();
- launcher.launchApp(this, metaData);
+ Class<Launcher> clazz = (Class<Launcher>) Thread.currentThread().getContextClassLoader().loadClass(launcherClassName);
+ launcher = clazz.newInstance();
+ launcher.launchApp(this, mainClassName, preloaderClassName);
} catch (Exception ex) {
throw new RuntimeException("Did not create correct launcher.", ex);
@@ -240,7 +444,11 @@
//surface ready now is time to launch javafx
getLauncherAndLaunchApplication();
} else {
- _surfaceChanged(surfaceDetails.surface);
+ try {
+ onSurfaceChangedNativeMethod1.invoke(null);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onSurfaceChangedNative1 method by reflection", e);
+ }
}
}
@@ -308,10 +516,11 @@
surfaceDetails = new SurfaceDetails(holder.getSurface(), format, width, height);
_setSurface(surfaceDetails.surface);
if (glassHasStarted) {
- _surfaceChanged(surfaceDetails.surface,
- surfaceDetails.format,
- surfaceDetails.width,
- surfaceDetails.height);
+ try {
+ onSurfaceChangedNativeMethod2.invoke(null, surfaceDetails.format, surfaceDetails.width, surfaceDetails.height);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onSurfaceChangedNative2 method by reflection", e);
+ }
}
}
@@ -321,7 +530,11 @@
surfaceDetails = new SurfaceDetails();
_setSurface(surfaceDetails.surface);
if (glassHasStarted) {
- _surfaceChanged(null);
+ try {
+ onSurfaceChangedNativeMethod1.invoke(null);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onSurfaceChangedNative1 method by reflection", e);
+ }
}
}
@@ -333,7 +546,11 @@
_setSurface(surfaceDetails.surface);
}
if (glassHasStarted) {
- _surfaceRedrawNeeded(surfaceDetails.surface);
+ try {
+ onSurfaceRedrawNeededNativeMethod.invoke(null);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onSurfaceRedrawNeededNative method by reflection", e);
+ }
}
}
@@ -350,14 +567,6 @@
private native void _setSurface(Surface surface);
- private native void _surfaceChanged(Surface surface);
-
- private native void _surfaceChanged(Surface surface, int format, int width, int height);
-
- private native void _surfaceRedrawNeeded(Surface surface);
-
- private native void _configurationChanged(int flag);
-
class DeviceConfiguration {
private static final int ORIENTATION_CHANGE = 1;
@@ -385,7 +594,12 @@
void dispatch() {
if ((change & ORIENTATION_CHANGE) > 0) {
Log.v(TAG, "Dispatching orientation change to");
- FXActivity.this._configurationChanged(SCREEN_ORIENTATION);
+ try {
+ onConfigurationChangedNativeMethod.invoke(null, SCREEN_ORIENTATION);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onConfigurationChangedNative method by reflection", e);
+ }
+
}
change = 0;
}
@@ -463,11 +677,11 @@
touchYs[0] = (int) event.getY();
}
Log.e(TAG, "call native MultitouchEvent");
- Platform.runLater(new Runnable() {
- public void run() {
- onMultiTouchEventNative(pcount, actions, ids, touchXs, touchYs);
- }
- });
+ try {
+ onMultiTouchEventMethod.invoke(null, pcount, actions, ids, touchXs, touchYs);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onMultiTouchEvent method by reflection", e);
+ }
return true;
}
@@ -476,19 +690,41 @@
if (!glassHasStarted) {
return false;
}
- Platform.runLater(new Runnable() {
- public void run() {
- onKeyEventNative(event.getAction(), event.getKeyCode(), event.getCharacters());
- }
- });
+ try {
+ onKeyEventMethod.invoke(null, event.getAction(), event.getKeyCode(), event.getCharacters());
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onKeyEventMethod method by reflection", e);
+ }
return true;
}
+ }
- private native void onMultiTouchEventNative(int count, int[] actions,
- int[] ids, int[] touchXs, int[] touchYs);
+ protected void setOnMultiTouchEventMethod(Method onMultiTouchEventMethod) {
+ this.onMultiTouchEventMethod = onMultiTouchEventMethod;
+ }
- private native void onKeyEventNative(int action, int keycode, String characters);
+ protected void setOnKeyEventMethod(Method onKeyEventMethod) {
+ this.onKeyEventMethod = onKeyEventMethod;
+ }
+ protected void setOnSurfaceChangedNativeMethod1(
+ Method onSurfaceChangedNativeMethod1) {
+ this.onSurfaceChangedNativeMethod1 = onSurfaceChangedNativeMethod1;
+ }
+
+ protected void setOnSurfaceChangedNativeMethod2(
+ Method onSurfaceChangedNativeMethod2) {
+ this.onSurfaceChangedNativeMethod2 = onSurfaceChangedNativeMethod2;
+ }
+
+ protected void setOnSurfaceRedrawNeededNativeMethod(
+ Method onSurfaceRedrawNeededNativeMethod) {
+ this.onSurfaceRedrawNeededNativeMethod = onSurfaceRedrawNeededNativeMethod;
+ }
+
+ protected void setOnConfigurationChangedNativeMethod(
+ Method onConfigurationChangedNativeMethod) {
+ this.onConfigurationChangedNativeMethod = onConfigurationChangedNativeMethod;
}
}
diff -r 1ed6990e4ff3 modules/graphics/src/dalvik/java/javafxports/android/Launcher.java
--- a/modules/graphics/src/dalvik/java/javafxports/android/Launcher.java Wed Jun 04 09:32:03 2014 +0200
+++ b/modules/graphics/src/dalvik/java/javafxports/android/Launcher.java Thu Jun 05 14:46:16 2014 +0200
@@ -26,8 +26,7 @@
package javafxports.android;
import android.app.Activity;
-import android.os.Bundle;
public interface Launcher {
- public void launchApp(Activity a, Bundle metadata);
+ public void launchApp(Activity a, String mainClassName, String preloaderClassName);
}
diff -r 1ed6990e4ff3 modules/graphics/src/main/java/com/sun/glass/ui/android/DalvikInput.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/android/DalvikInput.java Thu Jun 05 14:53:15 2014 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.glass.ui.android;
+
+import javafx.application.Platform;
+
+public class DalvikInput {
+
+ public static void onMultiTouchEvent(final int count, final int[] actions,
+ final int[] ids, final int[] touchXs, final int[] touchYs) {
+ Platform.runLater(new Runnable() {
+ public void run() {
+ onMultiTouchEventNative(count, actions, ids, touchXs, touchYs);
+ }
+ });
+
+ }
+
+ private static native void onMultiTouchEventNative(int count, int[] actions,
+ int[] ids, int[] touchXs, int[] touchYs);
+
+ public static void onKeyEvent(final int action, final int keycode, final String characters){
+ Platform.runLater(new Runnable() {
+ public void run() {
+ onKeyEventNative(action, keycode, characters);
+ }
+ });
+ };
+
+ private static native void onKeyEventNative(int action, int keycode, String characters);
+
+ public static native void onSurfaceChangedNative();
+
+ public static native void onSurfaceChangedNative(int format, int width, int height);
+
+ public static native void onSurfaceRedrawNeededNative();
+
+ public static native void onConfigurationChangedNative(int flag);
+}
Below are the changes for the Launcher and Activity classes, and a new class DalvikInput
diff -r 1ed6990e4ff3 modules/graphics/src/dalvik/java/javafxports/android/DalvikLauncher.java
--- a/modules/graphics/src/dalvik/java/javafxports/android/DalvikLauncher.java Wed Jun 04 09:32:03 2014 +0200
+++ b/modules/graphics/src/dalvik/java/javafxports/android/DalvikLauncher.java Thu Jun 05 14:46:16 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,16 +27,18 @@
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Bundle;
+import android.content.res.AssetManager;
import android.util.Log;
import dalvik.system.DexClassLoader;
+import dalvik.system.PathClassLoader;
+import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Map.Entry;
import java.util.Properties;
-import java.lang.ClassLoader;
public class DalvikLauncher implements Launcher {
@@ -45,8 +47,6 @@
private static final String COM_SUN_JAVAFX_APPLICATION_LAUNCHERIMPL = "com.sun.javafx.application.LauncherImpl";
private static final String LAUNCH_APPLICATION_METHOD = "launchApplication";
private static final String MAIN_METHOD = "main";
- private static final String META_DATA_MAIN_CLASS = "main.class";
- private static final String META_DATA_PRELOADER_CLASS = "preloader.class";
private static final String ANDROID_PROPERTY_PREFIX = "android.";
private static final Class[] LAUNCH_APPLICATION_ARGS = new Class[]{
@@ -59,14 +59,18 @@
private static boolean fxApplicationLaunching = false;
private Activity activity;
- private Bundle metadata;
+ private String preloaderClassName, mainClassName;
- public void launchApp(Activity a, Bundle metadata) {
+ public void launchApp(Activity a, String mainClassName, String preloaderClassName) {
this.activity = a;
- this.metadata = metadata;
+ this.preloaderClassName = preloaderClassName;
+ this.mainClassName = mainClassName;
+
+ InputStream is = null;
Properties userProperties = new Properties();
try {
- userProperties.load(DalvikLauncher.class.getResourceAsStream("/javafx.platform.properties"));
+ is = new BufferedInputStream(this.activity.getAssets().open("javafx.platform.properties"));
+ userProperties.load(is);
String key = null;
for (Entry<Object, Object> e : userProperties.entrySet()) {
key = (String) e.getKey();
@@ -78,14 +82,23 @@
} catch (IOException e) {
throw new RuntimeException("Can't load properties", e);
+ } finally {
+ try { is.close(); } catch(Exception e) {}
+
}
Log.v(TAG, "Launch JavaFX application on dalvik vm.");
try {
+ initMethodHandles();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to init method handles", e);
+ }
+
+ try {
final Class applicationClass = resolveApplicationClass();
final Class preloaderClass = resolvePreloaderClass();
- final Class javafxApplicationClass = Class.forName(JAVAFX_APPLICATION_APPLICATION);
- final Class javafxLauncherClass = Class.forName(COM_SUN_JAVAFX_APPLICATION_LAUNCHERIMPL);
+ final Class javafxApplicationClass = getApplicationClassLoader().loadClass(JAVAFX_APPLICATION_APPLICATION);
+ final Class javafxLauncherClass = getApplicationClassLoader().loadClass(COM_SUN_JAVAFX_APPLICATION_LAUNCHERIMPL);
final Method launchMethod = javafxLauncherClass.getMethod(
LAUNCH_APPLICATION_METHOD, LAUNCH_APPLICATION_ARGS);
@@ -127,42 +140,61 @@
Log.e(TAG, "Launch failed with exception.", e);
}
}
+
+ private static ClassLoader applicationClassLoader;
+
+ private ClassLoader getApplicationClassLoader() {
+ if (applicationClassLoader == null) {
+ // Internal storage where the DexClassLoader writes the optimized dex file to.
+ final File optimizedDexOutputPath = activity.getDir("outdex", Context.MODE_PRIVATE);
+ // Initialize the class loader with the secondary dex file.
+ // This includes the javafx, compatibility and application classes
+
+ ClassLoader cl = new DexClassLoader(FXActivity.dexClassPath,
+ optimizedDexOutputPath.getAbsolutePath(),
+ FXActivity.getInstance().getApplicationInfo().nativeLibraryDir,
+ activity.getClassLoader());
+ Thread.currentThread().setContextClassLoader(cl);
+ applicationClassLoader = cl;
+ Log.v(TAG, "Application classloader initialized: " + applicationClassLoader);
+ }
+ return applicationClassLoader;
+ }
+
+ private void initMethodHandles() throws ClassNotFoundException, NoSuchMethodException, SecurityException {
+ Class<?> dalvikInputClass = getApplicationClassLoader().loadClass("com.sun.glass.ui.android.DalvikInput");
+ FXActivity.getInstance().setOnMultiTouchEventMethod(dalvikInputClass.getMethod("onMultiTouchEvent", int.class, int[].class, int[].class, int[].class, int[].class));
+ FXActivity.getInstance().setOnKeyEventMethod(dalvikInputClass.getMethod("onKeyEvent", int.class, int.class, String.class));
+ FXActivity.getInstance().setOnSurfaceChangedNativeMethod1(dalvikInputClass.getMethod("onSurfaceChangedNative"));
+ FXActivity.getInstance().setOnSurfaceChangedNativeMethod2(dalvikInputClass.getMethod("onSurfaceChangedNative", int.class, int.class, int.class));
+ FXActivity.getInstance().setOnSurfaceRedrawNeededNativeMethod(dalvikInputClass.getMethod("onSurfaceRedrawNeededNative"));
+ FXActivity.getInstance().setOnConfigurationChangedNativeMethod(dalvikInputClass.getMethod("onConfigurationChangedNative", int.class));
+ }
+
private Class resolveApplicationClass()
throws PackageManager.NameNotFoundException, ClassNotFoundException {
- // Internal storage where the DexClassLoader writes the optimized dex file to.
- final File optimizedDexOutputPath = activity.getDir("outdex", Context.MODE_PRIVATE);
- final File dexInternalStoragePath = new File(activity.getDir("dex", Context.MODE_PRIVATE),
- FXActivity.APPLICATION_DEX_NAME);
-
- ClassLoader cl = null;
- if (dexInternalStoragePath.exists()) {
- // Initialize the class loader with the secondary dex file.
- cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
- optimizedDexOutputPath.getAbsolutePath(),
- null,
- activity.getClassLoader());
- } else {
- cl = activity.getClassLoader();
- }
+ ClassLoader cl = getApplicationClassLoader();
Class clazz = null;
-
- String applicationClassName = metadata.getString(META_DATA_MAIN_CLASS);
- if (applicationClassName != null && applicationClassName.length() > 0) {
- clazz = cl.loadClass(applicationClassName);
+ if (mainClassName != null && mainClassName.length() > 0) {
+ clazz = cl.loadClass(mainClassName);
}
+ // we set the contextClassLoader of the current thread to the one that loads the
+ // application code. Doing so, the FXMLLoader can resolve application classes
+ Thread.currentThread().setContextClassLoader(cl);
return clazz;
}
private Class resolvePreloaderClass()
throws PackageManager.NameNotFoundException, ClassNotFoundException {
+ ClassLoader cl = getApplicationClassLoader();
+
Class clazz = null;
- String className = metadata.getString(META_DATA_PRELOADER_CLASS);
- if (className != null && className.length() > 0) {
- clazz = Class.forName(className);
+ if (preloaderClassName != null && preloaderClassName.length() > 0) {
+ clazz = cl.loadClass(preloaderClassName);
}
return clazz;
}
diff -r 1ed6990e4ff3 modules/graphics/src/dalvik/java/javafxports/android/FXActivity.java
--- a/modules/graphics/src/dalvik/java/javafxports/android/FXActivity.java Wed Jun 04 09:32:03 2014 +0200
+++ b/modules/graphics/src/dalvik/java/javafxports/android/FXActivity.java Thu Jun 05 14:46:16 2014 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,11 +22,13 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package javafxports.android;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
@@ -46,14 +48,21 @@
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.util.Properties;
import java.util.concurrent.CountDownLatch;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
-import javafx.application.Platform;
+
public class FXActivity extends Activity implements SurfaceHolder.Callback,
SurfaceHolder.Callback2 {
@@ -63,10 +72,16 @@
private static final String ACTIVITY_LIB = "activity";
private static final String META_DATA_LAUNCHER_CLASS = "launcher.class";
private static final String DEFAULT_LAUNCHER_CLASS = "javafxports.android.DalvikLauncher";
+ private static final String META_DATA_MAIN_CLASS = "main.class";
+ private static final String META_DATA_PRELOADER_CLASS = "preloader.class";
private static final String META_DATA_DEBUG_PORT = "debug.port";
- public static final String APPLICATION_DEX_NAME = "Application_dex.jar";
- static final int BUF_SIZE = 8 * 1024;
+ private static final String APPLICATION_DEX_NAME = "Application_dex.jar";
+ private static final String APPLICATION_RESOURCES_NAME = "Application_resources.jar";
+ private static final String CLASSLOADER_PROPERTIES_NAME = "classloader.properties";
+ private static final String BUILD_TIME_NAME = "buildtime";
+ private static final int BUF_SIZE = 8 * 1024;
+ public static String dexClassPath = new String();
private static FXActivity instance;
private static Launcher launcher;
@@ -81,18 +96,44 @@
private static CountDownLatch cdlEvLoopFinished;
+ // Cache method handles
+ // Can not access com.sun.glass.ui.android.DalvikInput directly, because the javafx classes are loaded with a different classloader
+ private Method onMultiTouchEventMethod;
+ private Method onKeyEventMethod;
+ private Method onSurfaceChangedNativeMethod1;
+ private Method onSurfaceChangedNativeMethod2;
+ private Method onSurfaceRedrawNeededNativeMethod;
+ private Method onConfigurationChangedNativeMethod;
+
//configurations
private int SCREEN_ORIENTATION = 1;
+
+ private String launcherClassName;
+ private String mainClassName;
+ private String preloaderClassName;
+
+ private String currentBuildStamp;
+ private Properties classLoaderProperties;
+ private File dexInternalStoragePath;
+
+ private static final Bundle metadata = new Bundle();
static {
System.loadLibrary(ACTIVITY_LIB);
+ System.setProperty("javax.xml.stream.XMLInputFactory",
+ "com.sun.xml.stream.ZephyrParserFactory");
+ System.setProperty("javax.xml.stream.XMLOutputFactory",
+ "com.sun.xml.stream.ZephyrWriterFactory");
+ System.setProperty("javax.xml.stream.XMLEventFactory",
+ "com.sun.xml.stream.events.ZephyrEventFactory");
+
}
-
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Log.v(TAG, "onCreate");
+ Log.v(TAG, "onCreate FXActivity");
if (launcher != null) {
Log.v(TAG, "JavaFX application is already running");
return;
@@ -114,34 +155,22 @@
// Before the secondary dex file can be processed by the DexClassLoader,
// it has to be first copied from asset resource to a storage location.
- File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE), APPLICATION_DEX_NAME);
- BufferedInputStream bis = null;
- OutputStream dexWriter = null;
-
+ dexInternalStoragePath = getDir("dex", Context.MODE_PRIVATE);
+
try {
- bis = new BufferedInputStream(getAssets().open(APPLICATION_DEX_NAME));
- Log.v(TAG, "copy secondary dex file '" + APPLICATION_DEX_NAME + "' from asset resource to storage location");
- dexWriter = new BufferedOutputStream(new FileOutputStream(dexInternalStoragePath));
- byte[] buf = new byte[BUF_SIZE];
- int len;
- while ((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
- dexWriter.write(buf, 0, len);
+ if(isUptodate()) {
+ dexClassPath = (String) getClassLoaderProperties().get("classpath");
+ } else {
+ extractDexFiles(APPLICATION_DEX_NAME);
+ copy(APPLICATION_RESOURCES_NAME);
+ writeClassLoaderProperties();
}
} catch (FileNotFoundException e) {
- Log.v(TAG, "no secondary dex file '" + APPLICATION_DEX_NAME + "' found");
+ Log.v(TAG, "Not found application dex and resources jars.", e);
} catch (IOException e) {
- throw new RuntimeException("Failed to copy secondary dex file.", e);
- } finally {
- try {
- if (dexWriter != null) {
- dexWriter.close();
- }
- if (bis != null) {
- bis.close();
- }
- } catch (Exception e2) {
- }
+ throw new RuntimeException("Failed to copy a file.", e);
}
+ Log.v(TAG, "Secondary dex file classpath " + dexClassPath);
configuration = new DeviceConfiguration();
configuration.setConfiguration(getResources().getConfiguration());
Log.v(TAG, String.format("Confiuration orientation: %s",
@@ -154,32 +183,207 @@
jfxEventsLoop();
}
- private Bundle getMetadata() {
+ private boolean isUptodate() {
+
try {
+ String currentBuildTime = getCurrentBuildStamp();
+ if (currentBuildTime == null) {
+ return false;
+ }
+ Properties props = getClassLoaderProperties();
+ if(props == null) {
+ return false;
+ }
+ String extractedBuildTime = props.getProperty("buildStamp");
+ return currentBuildTime.equals(extractedBuildTime);
+ } catch (IOException e) {
+ Log.d(TAG, "Failed to compare build timestamps. reason:" + e.getMessage());
+ }
+ return false;
+ }
+
+ private String getCurrentBuildStamp() {
+ if(currentBuildStamp == null) {
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(getAssets().open(BUILD_TIME_NAME), "UTF-8"));
+ currentBuildStamp = reader.readLine();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to read build timestamp.", e);
+ } finally {
+ if(reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) { }
+ }
+ }
+ }
+ return currentBuildStamp;
+ }
+
+ private Properties getClassLoaderProperties() throws IOException {
+ if(classLoaderProperties == null) {
+ BufferedReader reader = null;
+ Properties props;
+ try {
+ props = new Properties();
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(dexInternalStoragePath, CLASSLOADER_PROPERTIES_NAME)), "UTF-8"));
+ props.load(reader);
+ } catch (FileNotFoundException e) {
+ return null;
+ } finally {
+ if(reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) { }
+ }
+ }
+ classLoaderProperties = props;
+ }
+ return classLoaderProperties;
+ }
+
+ private void extractDexFiles(String file) throws IOException {
+ BufferedInputStream bis = null;
+ ZipInputStream zis = null;
+ try {
+ Log.v(TAG, "extracting secondary dex files from '" + file + "' from asset resource to storage location");
+ bis = new BufferedInputStream(getAssets().open(file));
+ zis = new ZipInputStream(bis);
+ ZipEntry ze;
+ while ((ze = zis.getNextEntry()) != null) {
+ if(ze.getName().endsWith(".dex")) {
+ OutputStream dexWriter = null;
+ try {
+ File dexFile = new File(dexInternalStoragePath, ze.getName());
+ if(! dexClassPath.isEmpty()) {
+ dexClassPath += File.pathSeparator;
+ }
+ dexClassPath += dexFile.getAbsolutePath();
+ dexWriter = new BufferedOutputStream(new FileOutputStream(dexFile));
+ byte[] buf = new byte[BUF_SIZE];
+ int len;
+ while ((len = zis.read(buf, 0, BUF_SIZE)) > 0) {
+ dexWriter.write(buf, 0, len);
+ }
+ } finally {
+ if(dexWriter != null) { dexWriter.close(); };
+ }
+ }
+ }
+ } finally {
+ try {
+ if (zis != null) {
+ zis.close();
+ }
+ if (bis != null) {
+ bis.close();
+ }
+ } catch (Exception e2) {
+ }
+ }
+ }
+
+
+ private void copy(String file) throws IOException, FileNotFoundException {
+ BufferedOutputStream writer = null;
+ BufferedInputStream bis = null;
+ Log.v(TAG, "copy secondary resource file '" + file +
+ "' from asset resource to storage location");
+ bis = new BufferedInputStream(getAssets().open(file));
+ try {
+ File dexFile = new File(dexInternalStoragePath, file);
+ if(! dexClassPath.isEmpty()) {
+ dexClassPath += File.pathSeparator;
+ }
+ dexClassPath += dexFile.getAbsolutePath();
+ writer = new BufferedOutputStream(new FileOutputStream(dexFile));
+ byte[] buf = new byte[BUF_SIZE];
+ int len;
+ while ((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
+ writer.write(buf, 0, len);
+ }
+
+ } finally {
+ if(writer != null) { writer.close(); }
+ if (bis != null) {bis.close();}
+ }
+ }
+
+ private void writeClassLoaderProperties() {
+ Properties props = new Properties();
+ props.put("classpath", dexClassPath);
+ props.put("buildStamp", getCurrentBuildStamp());
+
+ BufferedOutputStream writer = null;
+ File propertyFile = new File(dexInternalStoragePath, CLASSLOADER_PROPERTIES_NAME);
+ Log.d(TAG, "writing " + propertyFile.getAbsolutePath());
+
+ try {
+ writer = new BufferedOutputStream(new FileOutputStream(propertyFile));
+ props.store(writer, null);
+ } catch (IOException e) {
+ Log.e(TAG, "failed to write " + propertyFile.getAbsolutePath(), e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) { }
+ }
+ }
+ }
+
+ private void getLauncherAndLaunchApplication() {
+ //load metadata
+ try {
+ ApplicationInfo appi = getPackageManager().getApplicationInfo(
+ getPackageName(), PackageManager.GET_META_DATA);
+ if (appi != null && appi.metaData != null) {
+ metadata.putAll(appi.metaData);
+ }
+
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Error getting Application info.");
+ }
+
+ try {
ActivityInfo ai = FXActivity.this.getPackageManager().getActivityInfo(
getIntent().getComponent(), PackageManager.GET_META_DATA);
- return ai.metaData;
+ if (ai != null && ai.metaData != null) {
+ metadata.putAll(ai.metaData);
+ }
} catch (NameNotFoundException e) {
- throw new RuntimeException("Error getting activity info", e);
+ Log.w(TAG, "Error getting Activity info.");
}
- }
-
- private void getLauncherAndLaunchApplication() {
- Bundle metaData = getMetadata();
- int dport = metaData.getInt(META_DATA_DEBUG_PORT);
+
+ int dport = metadata.getInt(META_DATA_DEBUG_PORT);
if (dport > 0) {
android.os.Debug.waitForDebugger();
}
- String launcherClass = metaData.getString(
- META_DATA_LAUNCHER_CLASS);
- if (launcherClass == null) {
- launcherClass = DEFAULT_LAUNCHER_CLASS;
+
+ launcherClassName = metadata.containsKey(META_DATA_LAUNCHER_CLASS) ?
+ metadata.getString(META_DATA_LAUNCHER_CLASS) : DEFAULT_LAUNCHER_CLASS;
+
+ mainClassName = metadata.containsKey(META_DATA_MAIN_CLASS) ?
+ metadata.getString(META_DATA_MAIN_CLASS) : null;
+
+ preloaderClassName = metadata.containsKey(META_DATA_PRELOADER_CLASS) ?
+ metadata.getString(META_DATA_PRELOADER_CLASS) : null;
+ if (mainClassName == null || mainClassName.length() == 0) {
+ throw new RuntimeException("Main application class must be defined.\n"
+ + "Use <meta-data android.name=\"main.class\" "
+ + "android.value=\"your.package.YourMainClass\"/>");
}
+ if (preloaderClassName != null && preloaderClassName.length() == 0) {
+ preloaderClassName = null;
+ }
+
+ //launch application
try {
- Class clazz = Class.forName(launcherClass);
- launcher = (Launcher) clazz.newInstance();
- launcher.launchApp(this, metaData);
+ Class<Launcher> clazz = (Class<Launcher>) Thread.currentThread().getContextClassLoader().loadClass(launcherClassName);
+ launcher = clazz.newInstance();
+ launcher.launchApp(this, mainClassName, preloaderClassName);
} catch (Exception ex) {
throw new RuntimeException("Did not create correct launcher.", ex);
@@ -240,7 +444,11 @@
//surface ready now is time to launch javafx
getLauncherAndLaunchApplication();
} else {
- _surfaceChanged(surfaceDetails.surface);
+ try {
+ onSurfaceChangedNativeMethod1.invoke(null);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onSurfaceChangedNative1 method by reflection", e);
+ }
}
}
@@ -308,10 +516,11 @@
surfaceDetails = new SurfaceDetails(holder.getSurface(), format, width, height);
_setSurface(surfaceDetails.surface);
if (glassHasStarted) {
- _surfaceChanged(surfaceDetails.surface,
- surfaceDetails.format,
- surfaceDetails.width,
- surfaceDetails.height);
+ try {
+ onSurfaceChangedNativeMethod2.invoke(null, surfaceDetails.format, surfaceDetails.width, surfaceDetails.height);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onSurfaceChangedNative2 method by reflection", e);
+ }
}
}
@@ -321,7 +530,11 @@
surfaceDetails = new SurfaceDetails();
_setSurface(surfaceDetails.surface);
if (glassHasStarted) {
- _surfaceChanged(null);
+ try {
+ onSurfaceChangedNativeMethod1.invoke(null);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onSurfaceChangedNative1 method by reflection", e);
+ }
}
}
@@ -333,7 +546,11 @@
_setSurface(surfaceDetails.surface);
}
if (glassHasStarted) {
- _surfaceRedrawNeeded(surfaceDetails.surface);
+ try {
+ onSurfaceRedrawNeededNativeMethod.invoke(null);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onSurfaceRedrawNeededNative method by reflection", e);
+ }
}
}
@@ -350,14 +567,6 @@
private native void _setSurface(Surface surface);
- private native void _surfaceChanged(Surface surface);
-
- private native void _surfaceChanged(Surface surface, int format, int width, int height);
-
- private native void _surfaceRedrawNeeded(Surface surface);
-
- private native void _configurationChanged(int flag);
-
class DeviceConfiguration {
private static final int ORIENTATION_CHANGE = 1;
@@ -385,7 +594,12 @@
void dispatch() {
if ((change & ORIENTATION_CHANGE) > 0) {
Log.v(TAG, "Dispatching orientation change to");
- FXActivity.this._configurationChanged(SCREEN_ORIENTATION);
+ try {
+ onConfigurationChangedNativeMethod.invoke(null, SCREEN_ORIENTATION);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onConfigurationChangedNative method by reflection", e);
+ }
+
}
change = 0;
}
@@ -463,11 +677,11 @@
touchYs[0] = (int) event.getY();
}
Log.e(TAG, "call native MultitouchEvent");
- Platform.runLater(new Runnable() {
- public void run() {
- onMultiTouchEventNative(pcount, actions, ids, touchXs, touchYs);
- }
- });
+ try {
+ onMultiTouchEventMethod.invoke(null, pcount, actions, ids, touchXs, touchYs);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onMultiTouchEvent method by reflection", e);
+ }
return true;
}
@@ -476,19 +690,41 @@
if (!glassHasStarted) {
return false;
}
- Platform.runLater(new Runnable() {
- public void run() {
- onKeyEventNative(event.getAction(), event.getKeyCode(), event.getCharacters());
- }
- });
+ try {
+ onKeyEventMethod.invoke(null, event.getAction(), event.getKeyCode(), event.getCharacters());
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to invoke com.sun.glass.ui.android.DalvikInput.onKeyEventMethod method by reflection", e);
+ }
return true;
}
+ }
- private native void onMultiTouchEventNative(int count, int[] actions,
- int[] ids, int[] touchXs, int[] touchYs);
+ protected void setOnMultiTouchEventMethod(Method onMultiTouchEventMethod) {
+ this.onMultiTouchEventMethod = onMultiTouchEventMethod;
+ }
- private native void onKeyEventNative(int action, int keycode, String characters);
+ protected void setOnKeyEventMethod(Method onKeyEventMethod) {
+ this.onKeyEventMethod = onKeyEventMethod;
+ }
+ protected void setOnSurfaceChangedNativeMethod1(
+ Method onSurfaceChangedNativeMethod1) {
+ this.onSurfaceChangedNativeMethod1 = onSurfaceChangedNativeMethod1;
+ }
+
+ protected void setOnSurfaceChangedNativeMethod2(
+ Method onSurfaceChangedNativeMethod2) {
+ this.onSurfaceChangedNativeMethod2 = onSurfaceChangedNativeMethod2;
+ }
+
+ protected void setOnSurfaceRedrawNeededNativeMethod(
+ Method onSurfaceRedrawNeededNativeMethod) {
+ this.onSurfaceRedrawNeededNativeMethod = onSurfaceRedrawNeededNativeMethod;
+ }
+
+ protected void setOnConfigurationChangedNativeMethod(
+ Method onConfigurationChangedNativeMethod) {
+ this.onConfigurationChangedNativeMethod = onConfigurationChangedNativeMethod;
}
}
diff -r 1ed6990e4ff3 modules/graphics/src/dalvik/java/javafxports/android/Launcher.java
--- a/modules/graphics/src/dalvik/java/javafxports/android/Launcher.java Wed Jun 04 09:32:03 2014 +0200
+++ b/modules/graphics/src/dalvik/java/javafxports/android/Launcher.java Thu Jun 05 14:46:16 2014 +0200
@@ -26,8 +26,7 @@
package javafxports.android;
import android.app.Activity;
-import android.os.Bundle;
public interface Launcher {
- public void launchApp(Activity a, Bundle metadata);
+ public void launchApp(Activity a, String mainClassName, String preloaderClassName);
}
diff -r 1ed6990e4ff3 modules/graphics/src/main/java/com/sun/glass/ui/android/DalvikInput.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/android/DalvikInput.java Thu Jun 05 14:53:15 2014 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.glass.ui.android;
+
+import javafx.application.Platform;
+
+public class DalvikInput {
+
+ public static void onMultiTouchEvent(final int count, final int[] actions,
+ final int[] ids, final int[] touchXs, final int[] touchYs) {
+ Platform.runLater(new Runnable() {
+ public void run() {
+ onMultiTouchEventNative(count, actions, ids, touchXs, touchYs);
+ }
+ });
+
+ }
+
+ private static native void onMultiTouchEventNative(int count, int[] actions,
+ int[] ids, int[] touchXs, int[] touchYs);
+
+ public static void onKeyEvent(final int action, final int keycode, final String characters){
+ Platform.runLater(new Runnable() {
+ public void run() {
+ onKeyEventNative(action, keycode, characters);
+ }
+ });
+ };
+
+ private static native void onKeyEventNative(int action, int keycode, String characters);
+
+ public static native void onSurfaceChangedNative();
+
+ public static native void onSurfaceChangedNative(int format, int width, int height);
+
+ public static native void onSurfaceRedrawNeededNative();
+
+ public static native void onConfigurationChangedNative(int flag);
+}