From 858fd433ca8d3978b4ecf18e6b8536a679a58ef8 Mon Sep 17 00:00:00 2001 From: iwgeric Date: Fri, 12 Dec 2014 08:42:13 -0500 Subject: [PATCH] Implement showSoftKeyboard for Android. Also changed JmeAndroidSystem to store the view instead of the activity. When users call JmeSystem.showSoftKeyboard(true), the default Android soft keyboard is displayed and any key presses are sent to jME as touch events. Key events are also generated so the user can use the same key listeners as Desktop to keep the user code common between Desktop and Android platforms. --- .../java/com/jme3/app/AndroidHarness.java | 11 +++--- .../jme3/asset/plugins/AndroidLocator.java | 12 +++---- .../input/android/AndroidSensorJoyInput.java | 18 +++++----- .../jme3/system/android/JmeAndroidSystem.java | 36 +++++++++++++------ .../com/jme3/system/android/OGLESContext.java | 33 ++++++++--------- 5 files changed, 62 insertions(+), 48 deletions(-) diff --git a/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java index b601e879f..d8dc6e7e9 100644 --- a/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java +++ b/jme3-android/src/main/java/com/jme3/app/AndroidHarness.java @@ -195,7 +195,6 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt logger.fine("onCreate"); super.onCreate(savedInstanceState); - JmeAndroidSystem.setActivity(this); if (screenFullScreen) { requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, @@ -231,7 +230,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt settings.setDepthBits(eglDepthBits); settings.setSamples(eglSamples); settings.setStencilBits(eglStencilBits); - + settings.setResolution(disp.getWidth(), disp.getHeight()); settings.setAudioRenderer(audioRendererType); @@ -252,7 +251,9 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt } ctx = (OGLESContext) app.getContext(); - view = ctx.createView(); + view = ctx.createView(this); + // store the glSurfaceView in JmeAndroidSystem for future use + JmeAndroidSystem.setView(view); // AndroidHarness wraps the app as a SystemListener. ctx.setSystemListener(this); layoutDisplay(); @@ -307,10 +308,10 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt } } setContentView(new TextView(this)); - JmeAndroidSystem.setActivity(null); ctx = null; app = null; view = null; + JmeAndroidSystem.setView(null); super.onDestroy(); } @@ -479,7 +480,7 @@ public class AndroidHarness extends Activity implements TouchListener, DialogInt if (app.getInputManager().hasMapping(SimpleApplication.INPUT_MAPPING_EXIT)) { app.getInputManager().deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT); } - + app.getInputManager().addMapping(ESCAPE_EVENT, new TouchTrigger(TouchInput.KEYCODE_BACK)); app.getInputManager().addListener(this, new String[]{ESCAPE_EVENT}); } diff --git a/jme3-android/src/main/java/com/jme3/asset/plugins/AndroidLocator.java b/jme3-android/src/main/java/com/jme3/asset/plugins/AndroidLocator.java index 4cff53d43..4c0bae82f 100644 --- a/jme3-android/src/main/java/com/jme3/asset/plugins/AndroidLocator.java +++ b/jme3-android/src/main/java/com/jme3/asset/plugins/AndroidLocator.java @@ -11,7 +11,7 @@ import java.util.logging.Logger; public class AndroidLocator implements AssetLocator { private static final Logger logger = Logger.getLogger(AndroidLocator.class.getName()); - + private android.content.res.AssetManager androidManager; private String rootPath = ""; @@ -25,7 +25,7 @@ public class AndroidLocator implements AssetLocator { this.assetPath = assetPath; this.in = in; } - + public AssetFileDescriptor openFileDescriptor() { try { return androidManager.openFd(assetPath); @@ -33,7 +33,7 @@ public class AndroidLocator implements AssetLocator { throw new AssetLoadException("Failed to open asset " + assetPath, ex); } } - + @Override public InputStream openStream() { if (in != null){ @@ -66,9 +66,9 @@ public class AndroidLocator implements AssetLocator { return null; } } - + public AndroidLocator() { - androidManager = JmeAndroidSystem.getActivity().getAssets(); + androidManager = JmeAndroidSystem.getView().getContext().getAssets(); } public void setRootPath(String rootPath) { @@ -89,7 +89,7 @@ public class AndroidLocator implements AssetLocator { return create(manager, key, assetPath); } catch (IOException ex) { // This is different handling than URL locator - // since classpath locating would return null at the getResource() + // since classpath locating would return null at the getResource() // call, otherwise there's a more critical error... throw new AssetLoadException("Failed to open asset " + assetPath, ex); } diff --git a/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java b/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java index 6ae7fca1d..034b068d8 100644 --- a/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java +++ b/jme3-android/src/main/java/com/jme3/input/android/AndroidSensorJoyInput.java @@ -32,7 +32,6 @@ package com.jme3.input.android; -import android.app.Activity; import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorEvent; @@ -40,7 +39,7 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Vibrator; import android.view.Surface; -import android.view.View; +import android.view.WindowManager; import com.jme3.input.AbstractJoystick; import com.jme3.input.DefaultJoystickAxis; import com.jme3.input.InputManager; @@ -98,9 +97,10 @@ import java.util.logging.Logger; public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { private final static Logger logger = Logger.getLogger(AndroidSensorJoyInput.class.getName()); - private Activity activity = null; + private Context context = null; private InputManager inputManager = null; private SensorManager sensorManager = null; + private WindowManager windowManager = null; private Vibrator vibrator = null; private boolean vibratorActive = false; private long maxRumbleTime = 250; // 250ms @@ -135,11 +135,13 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { } private void initSensorManager() { - this.activity = JmeAndroidSystem.getActivity(); + this.context = JmeAndroidSystem.getView().getContext(); + // Get instance of the WindowManager from the current Context + windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); // Get instance of the SensorManager from the current Context - sensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE); + sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); // Get instance of Vibrator from current Context - vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); + vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); if (vibrator == null) { logger.log(Level.FINE, "Vibrator Service not found."); } @@ -306,7 +308,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { * @return Current device rotation amount */ private int getScreenRotation() { - return activity.getWindowManager().getDefaultDisplay().getRotation(); + return windowManager.getDefaultDisplay().getRotation(); } /** @@ -592,7 +594,7 @@ public class AndroidSensorJoyInput implements JoyInput, SensorEventListener { joysticks = null; sensorManager = null; vibrator = null; - activity = null; + context = null; } public boolean isInitialized() { diff --git a/jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java b/jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java index a491a4158..26029c7bf 100644 --- a/jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java +++ b/jme3-android/src/main/java/com/jme3/system/android/JmeAndroidSystem.java @@ -1,10 +1,11 @@ package com.jme3.system.android; -import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.graphics.Bitmap; import android.os.Environment; +import android.view.View; +import android.view.inputmethod.InputMethodManager; import com.jme3.asset.AndroidAssetManager; import com.jme3.asset.AndroidImageInfo; import com.jme3.asset.AssetManager; @@ -31,7 +32,7 @@ import java.util.logging.Level; public class JmeAndroidSystem extends JmeSystemDelegate { - private static Activity activity; + private static View view; private static String audioRendererType = AppSettings.ANDROID_OPENAL_SOFT; static { @@ -82,9 +83,9 @@ public class JmeAndroidSystem extends JmeSystemDelegate { public void showErrorDialog(String message) { final String finalMsg = message; final String finalTitle = "Error in application"; - final Activity context = JmeAndroidSystem.getActivity(); + final Context context = JmeAndroidSystem.getView().getContext(); - context.runOnUiThread(new Runnable() { + view.getHandler().post(new Runnable() { @Override public void run() { AlertDialog dialog = new AlertDialog.Builder(context) @@ -184,7 +185,7 @@ public class JmeAndroidSystem extends JmeSystemDelegate { // The files can only be accessed by this application storageFolder = storageFolders.get(type); if (storageFolder == null) { - storageFolder = activity.getApplicationContext().getDir("", Context.MODE_PRIVATE); + storageFolder = view.getContext().getDir("", Context.MODE_PRIVATE); storageFolders.put(type, storageFolder); } break; @@ -203,7 +204,7 @@ public class JmeAndroidSystem extends JmeSystemDelegate { String state = Environment.getExternalStorageState(); logger.log(Level.FINE, "ExternalStorageState: {0}", state); if (state.equals(Environment.MEDIA_MOUNTED)) { - storageFolder = activity.getApplicationContext().getExternalFilesDir(null); + storageFolder = view.getContext().getExternalFilesDir(null); storageFolders.put(type, storageFolder); } } @@ -219,12 +220,12 @@ public class JmeAndroidSystem extends JmeSystemDelegate { return storageFolder; } - public static void setActivity(Activity activity) { - JmeAndroidSystem.activity = activity; + public static void setView(View view) { + JmeAndroidSystem.view = view; } - public static Activity getActivity() { - return activity; + public static View getView() { + return view; } public static String getAudioRendererType() { @@ -232,6 +233,19 @@ public class JmeAndroidSystem extends JmeSystemDelegate { } @Override - public void showSoftKeyboard(boolean show) { + public void showSoftKeyboard(final boolean show) { + view.getHandler().post(new Runnable() { + + public void run() { + InputMethodManager manager = + (InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + + if (show) { + manager.showSoftInput(view, 0); + } else { + manager.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + }); } } diff --git a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java index e669530a9..79ec34875 100644 --- a/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java +++ b/jme3-android/src/main/java/com/jme3/system/android/OGLESContext.java @@ -31,7 +31,6 @@ */ package com.jme3.system.android; -import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; import android.content.Context; @@ -42,6 +41,7 @@ import android.opengl.GLSurfaceView; import android.os.Build; import android.text.InputType; import android.view.Gravity; +import android.view.View; import android.view.ViewGroup.LayoutParams; import android.widget.EditText; import android.widget.FrameLayout; @@ -56,7 +56,6 @@ import com.jme3.system.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; -import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; @@ -96,21 +95,19 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex * * @return GLSurfaceView The newly created view */ - public GLSurfaceView createView() { - Context appContext = JmeAndroidSystem.getActivity().getApplication(); - + public GLSurfaceView createView(Context context) { // NOTE: We assume all ICS devices have OpenGL ES 2.0. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { // below 4.0, check OpenGL ES 2.0 support. - ActivityManager am = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); ConfigurationInfo info = am.getDeviceConfigurationInfo(); if (info.reqGlEsVersion < 0x20000) { throw new UnsupportedOperationException("OpenGL ES 2.0 is not supported on this device"); } } - + // Start to set up the view - GLSurfaceView view = new GLSurfaceView(appContext); + GLSurfaceView view = new GLSurfaceView(context); if (androidInput == null) { androidInput = new AndroidInputHandler(); } @@ -123,7 +120,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex view.setFocusableInTouchMode(true); view.setFocusable(true); - + // setFormat must be set before AndroidConfigChooser is called by the surfaceview. // if setFormat is called after ConfigChooser is called, then execution // stops at the setFormat call without a crash. @@ -148,18 +145,18 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex AndroidConfigChooser configChooser = new AndroidConfigChooser(settings); view.setEGLConfigChooser(configChooser); view.setRenderer(this); - + // Attempt to preserve the EGL Context on app pause/resume. - // Not destroying and recreating the EGL context + // Not destroying and recreating the EGL context // will help with resume time by reusing the existing context to avoid // reloading all the OpenGL objects. if (Build.VERSION.SDK_INT >= 11) { view.setPreserveEGLContextOnPause(true); } - + return view; } - + // renderer:initialize @Override public void onSurfaceCreated(GL10 gl, EGLConfig cfg) { @@ -388,13 +385,13 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex logger.log(Level.FINE, "requestDialog: title: {0}, initialValue: {1}", new Object[]{title, initialValue}); - final Activity activity = JmeAndroidSystem.getActivity(); - activity.runOnUiThread(new Runnable() { + final View view = JmeAndroidSystem.getView(); + view.getHandler().post(new Runnable() { @Override public void run() { - final FrameLayout layoutTextDialogInput = new FrameLayout(activity); - final EditText editTextDialogInput = new EditText(activity); + final FrameLayout layoutTextDialogInput = new FrameLayout(view.getContext()); + final EditText editTextDialogInput = new EditText(view.getContext()); editTextDialogInput.setWidth(LayoutParams.FILL_PARENT); editTextDialogInput.setHeight(LayoutParams.FILL_PARENT); editTextDialogInput.setPadding(20, 20, 20, 20); @@ -425,7 +422,7 @@ public class OGLESContext implements JmeContext, GLSurfaceView.Renderer, SoftTex layoutTextDialogInput.addView(editTextDialogInput); - AlertDialog dialogTextInput = new AlertDialog.Builder(activity).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK", + AlertDialog dialogTextInput = new AlertDialog.Builder(view.getContext()).setTitle(title).setView(layoutTextDialogInput).setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { /* User clicked OK, send COMPLETE action