# HG changeset patch # User Martin Gerhardy # Date 1366211197 -7200 # Node ID c39ef52ee8d1676ce11e228ceb28758a7c8565e5 # Parent 873715d91f835a4b13c5a2d896c3edc58af543ae * android: extracted to own classes * updated javadoc * format stuff * made some stuff protected diff --git a/android-project/src/org/libsdl/app/DummyEdit.java b/android-project/src/org/libsdl/app/DummyEdit.java new file mode 100644 --- /dev/null +++ b/android-project/src/org/libsdl/app/DummyEdit.java @@ -0,0 +1,57 @@ +package org.libsdl.app; + +import android.content.Context; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +/** + * This is a fake invisible editor view that receives the input and defines the + * pan&scan region + */ +class DummyEdit extends View implements View.OnKeyListener { + InputConnection ic; + + public DummyEdit(final Context context) { + super(context); + setFocusableInTouchMode(true); + setFocusable(true); + setOnKeyListener(this); + } + + @Override + public boolean onCheckIsTextEditor() { + return true; + } + + public boolean onKey(final View v, final int keyCode, final KeyEvent event) { + // This handles the hardware keyboard input + if (event.isPrintingKey()) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); + } + return true; + } + + if (event.getAction() == KeyEvent.ACTION_DOWN) { + SDLActivity.onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + SDLActivity.onNativeKeyUp(keyCode); + return true; + } + + return false; + } + + @Override + public InputConnection onCreateInputConnection(final EditorInfo outAttrs) { + ic = new SDLInputConnection(this, true); + + // API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN + outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | 33554432; + + return ic; + } +} \ No newline at end of file diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java --- a/android-project/src/org/libsdl/app/SDLActivity.java +++ b/android-project/src/org/libsdl/app/SDLActivity.java @@ -1,745 +1,438 @@ package org.libsdl.app; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.opengles.GL10; -import javax.microedition.khronos.egl.*; +import javax.microedition.khronos.egl.EGLDisplay; +import javax.microedition.khronos.egl.EGLSurface; -import android.app.*; -import android.content.*; -import android.view.*; -import android.view.inputmethod.BaseInputConnection; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; +import android.app.Activity; +import android.content.Context; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioTrack; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.AbsoluteLayout; -import android.os.*; -import android.util.Log; -import android.graphics.*; -import android.text.method.*; -import android.text.*; -import android.media.*; -import android.hardware.*; -import android.content.*; - -import java.lang.*; - /** - SDL Activity -*/ + * SDL Activity + */ public class SDLActivity extends Activity { - // Keep track of the paused state - public static boolean mIsPaused; + // Keep track of the paused state + public static boolean mIsPaused; - // Main components - private static SDLActivity mSingleton; - private static SDLSurface mSurface; - private static View mTextEdit; - private static ViewGroup mLayout; + // Main components + private static SDLActivity mSingleton; + private static SDLSurface mSurface; + static View mTextEdit; + static ViewGroup mLayout; - // This is what SDL runs in. It invokes SDL_main(), eventually - private static Thread mSDLThread; + // This is what SDL runs in. It invokes SDL_main(), eventually + private static Thread mSDLThread; - // Audio - private static Thread mAudioThread; - private static AudioTrack mAudioTrack; + // Audio + private static Thread mAudioThread; + private static AudioTrack mAudioTrack; - // EGL private objects - private static EGLContext mEGLContext; - private static EGLSurface mEGLSurface; - private static EGLDisplay mEGLDisplay; - private static EGLConfig mEGLConfig; - private static int mGLMajor, mGLMinor; + // EGL private objects + private static EGLContext mEGLContext; + private static EGLSurface mEGLSurface; + private static EGLDisplay mEGLDisplay; + private static EGLConfig mEGLConfig; + private static int mGLMajor, mGLMinor; - // Load the .so - static { - System.loadLibrary("SDL2"); - //System.loadLibrary("SDL2_image"); - //System.loadLibrary("SDL2_mixer"); - //System.loadLibrary("SDL2_ttf"); - System.loadLibrary("main"); - } + // Load the .so + static { + System.loadLibrary("SDL2"); + // System.loadLibrary("SDL2_image"); + // System.loadLibrary("SDL2_mixer"); + // System.loadLibrary("SDL2_ttf"); + System.loadLibrary("main"); + } - // Setup - protected void onCreate(Bundle savedInstanceState) { - //Log.v("SDL", "onCreate()"); - super.onCreate(savedInstanceState); - - // So we can call stuff from static callbacks - mSingleton = this; + // Setup + protected void onCreate(final Bundle savedInstanceState) { + // Log.v("SDL", "onCreate()"); + super.onCreate(savedInstanceState); - // Keep track of the paused state - mIsPaused = false; + // So we can call stuff from static callbacks + mSingleton = this; - // Set up the surface - mSurface = new SDLSurface(getApplication()); + // Keep track of the paused state + mIsPaused = false; - mLayout = new AbsoluteLayout(this); - mLayout.addView(mSurface); + // Set up the surface + mSurface = new SDLSurface(getApplication()); - setContentView(mLayout); + mLayout = getLayout(); + mLayout.addView(mSurface); - SurfaceHolder holder = mSurface.getHolder(); - } + setContentView(mLayout); + } - // Events - /*protected void onPause() { - Log.v("SDL", "onPause()"); - super.onPause(); - // Don't call SDLActivity.nativePause(); here, it will be called by SDLSurface::surfaceDestroyed - } + protected ViewGroup getLayout() { + return new AbsoluteLayout(this); + } - protected void onResume() { - Log.v("SDL", "onResume()"); - super.onResume(); - // Don't call SDLActivity.nativeResume(); here, it will be called via SDLSurface::surfaceChanged->SDLActivity::startApp - }*/ + // Events + /* + * protected void onPause() { Log.v("SDL", "onPause()"); super.onPause(); // + * Don't call SDLActivity.nativePause(); here, it will be called by + * SDLSurface::surfaceDestroyed } + * + * protected void onResume() { Log.v("SDL", "onResume()"); super.onResume(); + * // Don't call SDLActivity.nativeResume(); here, it will be called via + * SDLSurface::surfaceChanged->SDLActivity::startApp } + */ - protected void onDestroy() { - super.onDestroy(); - Log.v("SDL", "onDestroy()"); - // Send a quit message to the application - SDLActivity.nativeQuit(); + protected void onDestroy() { + super.onDestroy(); + Log.v("SDL", "onDestroy()"); + // Send a quit message to the application + SDLActivity.nativeQuit(); - // Now wait for the SDL thread to quit - if (mSDLThread != null) { - try { - mSDLThread.join(); - } catch(Exception e) { - Log.v("SDL", "Problem stopping thread: " + e); - } - mSDLThread = null; + // Now wait for the SDL thread to quit + if (mSDLThread != null) { + try { + mSDLThread.join(); + } catch (final Exception e) { + Log.v("SDL", "Problem stopping thread: " + e); + } + mSDLThread = null; - //Log.v("SDL", "Finished waiting for SDL thread"); - } - } + // Log.v("SDL", "Finished waiting for SDL thread"); + } + } - // Messages from the SDLMain thread - static final int COMMAND_CHANGE_TITLE = 1; - static final int COMMAND_UNUSED = 2; - static final int COMMAND_TEXTEDIT_HIDE = 3; + // Messages from the SDLMain thread + static final int COMMAND_CHANGE_TITLE = 1; + static final int COMMAND_UNUSED = 2; + static final int COMMAND_TEXTEDIT_HIDE = 3; - // Handler for the messages - Handler commandHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.arg1) { - case COMMAND_CHANGE_TITLE: - setTitle((String)msg.obj); - break; - case COMMAND_TEXTEDIT_HIDE: - if (mTextEdit != null) { - mTextEdit.setVisibility(View.GONE); + // Handler for the messages + Handler commandHandler = new Handler() { + @Override + public void handleMessage(final Message msg) { + switch (msg.arg1) { + case COMMAND_CHANGE_TITLE: + setTitle((String) msg.obj); + break; + case COMMAND_TEXTEDIT_HIDE: + if (mTextEdit != null) { + mTextEdit.setVisibility(View.GONE); - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); - } - break; - } - } - }; + final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0); + } + break; + } + } + }; - // Send a message from the SDLMain thread - void sendCommand(int command, Object data) { - Message msg = commandHandler.obtainMessage(); - msg.arg1 = command; - msg.obj = data; - commandHandler.sendMessage(msg); - } + // Send a message from the SDLMain thread + protected void sendCommand(final int command, final Object data) { + final Message msg = commandHandler.obtainMessage(); + msg.arg1 = command; + msg.obj = data; + commandHandler.sendMessage(msg); + } - // C functions we call - public static native void nativeInit(); - public static native void nativeQuit(); - public static native void nativePause(); - public static native void nativeResume(); - public static native void onNativeResize(int x, int y, int format); - public static native void onNativeKeyDown(int keycode); - public static native void onNativeKeyUp(int keycode); - public static native void onNativeTouch(int touchDevId, int pointerFingerId, - int action, float x, - float y, float p); - public static native void onNativeAccel(float x, float y, float z); - public static native void nativeRunAudioThread(); + // C functions we call + public static native void nativeInit(); + public static native void nativeQuit(); - // Java functions called from C + public static native void nativePause(); - public static boolean createGLContext(int majorVersion, int minorVersion, int[] attribs) { - return initEGL(majorVersion, minorVersion, attribs); - } + public static native void nativeResume(); - public static void flipBuffers() { - flipEGL(); - } + public static native void onNativeResize(int x, int y, int format); - public static void setActivityTitle(String title) { - // Called from SDLMain() thread and can't directly affect the view - mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); - } + public static native void onNativeKeyDown(int keycode); - public static void sendMessage(int command, int param) { - mSingleton.sendCommand(command, Integer.valueOf(param)); - } + public static native void onNativeKeyUp(int keycode); - public static Context getContext() { - return mSingleton; - } + public static native void onNativeTouch(int touchDevId, + int pointerFingerId, int action, float x, float y, float p); - public static void startApp() { - // Start up the C app thread - if (mSDLThread == null) { - mSDLThread = new Thread(new SDLMain(), "SDLThread"); - mSDLThread.start(); - } - else { - /* - * Some Android variants may send multiple surfaceChanged events, so we don't need to resume every time - * every time we get one of those events, only if it comes after surfaceDestroyed - */ - if (mIsPaused) { - SDLActivity.nativeResume(); - SDLActivity.mIsPaused = false; - } - } - } - - static class ShowTextInputHandler implements Runnable { - /* - * This is used to regulate the pan&scan method to have some offset from - * the bottom edge of the input region and the top edge of an input - * method (soft keyboard) - */ - static final int HEIGHT_PADDING = 15; + public static native void onNativeAccel(float x, float y, float z); - public int x, y, w, h; + public static native void nativeRunAudioThread(); - public ShowTextInputHandler(int x, int y, int w, int h) { - this.x = x; - this.y = y; - this.w = w; - this.h = h; - } + // Java functions called from C - public void run() { - AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams( - w, h + HEIGHT_PADDING, x, y); + public static boolean createGLContext(final int majorVersion, final int minorVersion, + final int[] attribs) { + return initEGL(majorVersion, minorVersion, attribs); + } - if (mTextEdit == null) { - mTextEdit = new DummyEdit(getContext()); + public static void flipBuffers() { + flipEGL(); + } - mLayout.addView(mTextEdit, params); - } else { - mTextEdit.setLayoutParams(params); - } + public static void setActivityTitle(final String title) { + // Called from SDLMain() thread and can't directly affect the view + mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); + } - mTextEdit.setVisibility(View.VISIBLE); - mTextEdit.requestFocus(); + public static void sendMessage(final int command, final int param) { + mSingleton.sendCommand(command, Integer.valueOf(param)); + } - InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(mTextEdit, 0); - } + public static Context getContext() { + return mSingleton; + } - } + public static void startApp() { + // Start up the C app thread + if (mSDLThread == null) { + mSDLThread = new Thread(new SDLMain(), "SDLThread"); + mSDLThread.start(); + } else { + /* + * Some Android variants may send multiple surfaceChanged events, so + * we don't need to resume every time every time we get one of those + * events, only if it comes after surfaceDestroyed + */ + if (mIsPaused) { + SDLActivity.nativeResume(); + SDLActivity.mIsPaused = false; + } + } + } - public static void showTextInput(int x, int y, int w, int h) { - // Transfer the task to the main thread as a Runnable - mSingleton.commandHandler.post(new ShowTextInputHandler(x, y, w, h)); - } + public static void showTextInput(final int x, final int y, final int w, final int h) { + // Transfer the task to the main thread as a Runnable + mSingleton.commandHandler.post(new ShowTextInputHandler(x, y, w, h)); + } + // EGL functions + public static boolean initEGL(final int majorVersion, final int minorVersion, + final int[] attribs) { + try { + if (SDLActivity.mEGLDisplay == null) { + Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + + minorVersion); - // EGL functions - public static boolean initEGL(int majorVersion, int minorVersion, int[] attribs) { - try { - if (SDLActivity.mEGLDisplay == null) { - Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion); + final EGL10 egl = (EGL10) EGLContext.getEGL(); - EGL10 egl = (EGL10)EGLContext.getEGL(); + final EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + final int[] version = new int[2]; + egl.eglInitialize(dpy, version); - int[] version = new int[2]; - egl.eglInitialize(dpy, version); + final EGLConfig[] configs = new EGLConfig[1]; + final int[] num_config = new int[1]; + if (!egl.eglChooseConfig(dpy, attribs, configs, 1, num_config) + || num_config[0] == 0) { + Log.e("SDL", "No EGL config available"); + return false; + } + final EGLConfig config = configs[0]; - EGLConfig[] configs = new EGLConfig[1]; - int[] num_config = new int[1]; - if (!egl.eglChooseConfig(dpy, attribs, configs, 1, num_config) || num_config[0] == 0) { - Log.e("SDL", "No EGL config available"); - return false; - } - EGLConfig config = configs[0]; + SDLActivity.mEGLDisplay = dpy; + SDLActivity.mEGLConfig = config; + SDLActivity.mGLMajor = majorVersion; + SDLActivity.mGLMinor = minorVersion; + } + return SDLActivity.createEGLSurface(); - SDLActivity.mEGLDisplay = dpy; - SDLActivity.mEGLConfig = config; - SDLActivity.mGLMajor = majorVersion; - SDLActivity.mGLMinor = minorVersion; - } - return SDLActivity.createEGLSurface(); + } catch (final Exception e) { + Log.v("SDL", e + ""); + for (final StackTraceElement s : e.getStackTrace()) { + Log.v("SDL", s.toString()); + } + return false; + } + } - } catch(Exception e) { - Log.v("SDL", e + ""); - for (StackTraceElement s : e.getStackTrace()) { - Log.v("SDL", s.toString()); - } - return false; - } - } + public static boolean createEGLContext() { + final EGL10 egl = (EGL10) EGLContext.getEGL(); + final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + final int contextAttrs[] = new int[] { EGL_CONTEXT_CLIENT_VERSION, + SDLActivity.mGLMajor, EGL10.EGL_NONE }; + SDLActivity.mEGLContext = egl.eglCreateContext(SDLActivity.mEGLDisplay, + SDLActivity.mEGLConfig, EGL10.EGL_NO_CONTEXT, contextAttrs); + if (SDLActivity.mEGLContext == EGL10.EGL_NO_CONTEXT) { + Log.e("SDL", "Couldn't create context"); + return false; + } + return true; + } - public static boolean createEGLContext() { - EGL10 egl = (EGL10)EGLContext.getEGL(); - int EGL_CONTEXT_CLIENT_VERSION=0x3098; - int contextAttrs[] = new int[] { EGL_CONTEXT_CLIENT_VERSION, SDLActivity.mGLMajor, EGL10.EGL_NONE }; - SDLActivity.mEGLContext = egl.eglCreateContext(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, EGL10.EGL_NO_CONTEXT, contextAttrs); - if (SDLActivity.mEGLContext == EGL10.EGL_NO_CONTEXT) { - Log.e("SDL", "Couldn't create context"); - return false; - } - return true; - } + public static boolean createEGLSurface() { + if (SDLActivity.mEGLDisplay != null && SDLActivity.mEGLConfig != null) { + final EGL10 egl = (EGL10) EGLContext.getEGL(); + if (SDLActivity.mEGLContext == null) + createEGLContext(); - public static boolean createEGLSurface() { - if (SDLActivity.mEGLDisplay != null && SDLActivity.mEGLConfig != null) { - EGL10 egl = (EGL10)EGLContext.getEGL(); - if (SDLActivity.mEGLContext == null) createEGLContext(); + Log.v("SDL", "Creating new EGL Surface"); + final EGLSurface surface = egl.eglCreateWindowSurface( + SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, + SDLActivity.mSurface, null); + if (surface == EGL10.EGL_NO_SURFACE) { + Log.e("SDL", "Couldn't create surface"); + return false; + } - Log.v("SDL", "Creating new EGL Surface"); - EGLSurface surface = egl.eglCreateWindowSurface(SDLActivity.mEGLDisplay, SDLActivity.mEGLConfig, SDLActivity.mSurface, null); - if (surface == EGL10.EGL_NO_SURFACE) { - Log.e("SDL", "Couldn't create surface"); - return false; - } + if (egl.eglGetCurrentContext() != SDLActivity.mEGLContext) { + if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, + surface, SDLActivity.mEGLContext)) { + Log.e("SDL", + "Old EGL Context doesnt work, trying with a new one"); + // TODO: Notify the user via a message that the old context + // could not be restored, and that textures need to be + // manually restored. + createEGLContext(); + if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, + surface, SDLActivity.mEGLContext)) { + Log.e("SDL", "Failed making EGL Context current"); + return false; + } + } + } + SDLActivity.mEGLSurface = surface; + return true; + } else { + Log.e("SDL", "Surface creation failed, display = " + + SDLActivity.mEGLDisplay + ", config = " + + SDLActivity.mEGLConfig); + return false; + } + } - if (egl.eglGetCurrentContext() != SDLActivity.mEGLContext) { - if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) { - Log.e("SDL", "Old EGL Context doesnt work, trying with a new one"); - // TODO: Notify the user via a message that the old context could not be restored, and that textures need to be manually restored. - createEGLContext(); - if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) { - Log.e("SDL", "Failed making EGL Context current"); - return false; - } - } - } - SDLActivity.mEGLSurface = surface; - return true; - } else { - Log.e("SDL", "Surface creation failed, display = " + SDLActivity.mEGLDisplay + ", config = " + SDLActivity.mEGLConfig); - return false; - } - } + // EGL buffer flip + public static void flipEGL() { + try { + final EGL10 egl = (EGL10) EGLContext.getEGL(); - // EGL buffer flip - public static void flipEGL() { - try { - EGL10 egl = (EGL10)EGLContext.getEGL(); + egl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, null); - egl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, null); + // drawing here - // drawing here + egl.eglWaitGL(); - egl.eglWaitGL(); + egl.eglSwapBuffers(SDLActivity.mEGLDisplay, SDLActivity.mEGLSurface); - egl.eglSwapBuffers(SDLActivity.mEGLDisplay, SDLActivity.mEGLSurface); + } catch (final Exception e) { + Log.v("SDL", "flipEGL(): " + e); + for (final StackTraceElement s : e.getStackTrace()) { + Log.v("SDL", s.toString()); + } + } + } + // Audio + public static void audioInit(final int sampleRate, final boolean is16Bit, + final boolean isStereo, int desiredFrames) { + final int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO + : AudioFormat.CHANNEL_CONFIGURATION_MONO; + final int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT + : AudioFormat.ENCODING_PCM_8BIT; + final int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); - } catch(Exception e) { - Log.v("SDL", "flipEGL(): " + e); - for (StackTraceElement s : e.getStackTrace()) { - Log.v("SDL", s.toString()); - } - } - } + Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + + " " + (is16Bit ? "16-bit" : "8-bit") + " " + + ((float) sampleRate / 1000f) + "kHz, " + desiredFrames + + " frames buffer"); - // Audio - public static void audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) { - int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO; - int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; - int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); - - Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); - - // Let the user pick a larger buffer if they really want -- but ye - // gods they probably shouldn't, the minimums are horrifyingly high - // latency already - desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); - - mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, - channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); - - audioStartThread(); - - Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer"); - } - - public static void audioStartThread() { - mAudioThread = new Thread(new Runnable() { - public void run() { - mAudioTrack.play(); - nativeRunAudioThread(); - } - }); - - // I'd take REALTIME if I could get it! - mAudioThread.setPriority(Thread.MAX_PRIORITY); - mAudioThread.start(); - } - - public static void audioWriteShortBuffer(short[] buffer) { - for (int i = 0; i < buffer.length; ) { - int result = mAudioTrack.write(buffer, i, buffer.length - i); - if (result > 0) { - i += result; - } else if (result == 0) { - try { - Thread.sleep(1); - } catch(InterruptedException e) { - // Nom nom - } - } else { - Log.w("SDL", "SDL audio: error return from write(short)"); - return; - } - } - } - - public static void audioWriteByteBuffer(byte[] buffer) { - for (int i = 0; i < buffer.length; ) { - int result = mAudioTrack.write(buffer, i, buffer.length - i); - if (result > 0) { - i += result; - } else if (result == 0) { - try { - Thread.sleep(1); - } catch(InterruptedException e) { - // Nom nom - } - } else { - Log.w("SDL", "SDL audio: error return from write(short)"); - return; - } - } - } + // Let the user pick a larger buffer if they really want -- but ye + // gods they probably shouldn't, the minimums are horrifyingly high + // latency already + desiredFrames = Math.max( + desiredFrames, + (AudioTrack.getMinBufferSize(sampleRate, channelConfig, + audioFormat) + frameSize - 1) + / frameSize); - public static void audioQuit() { - if (mAudioThread != null) { - try { - mAudioThread.join(); - } catch(Exception e) { - Log.v("SDL", "Problem stopping audio thread: " + e); - } - mAudioThread = null; + mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, + channelConfig, audioFormat, desiredFrames * frameSize, + AudioTrack.MODE_STREAM); - //Log.v("SDL", "Finished waiting for audio thread"); - } + audioStartThread(); - if (mAudioTrack != null) { - mAudioTrack.stop(); - mAudioTrack = null; - } - } + Log.v("SDL", + "SDL audio: got " + + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" + : "mono") + + " " + + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" + : "8-bit") + " " + + ((float) mAudioTrack.getSampleRate() / 1000f) + + "kHz, " + desiredFrames + " frames buffer"); + } + + public static void audioStartThread() { + mAudioThread = new Thread(new Runnable() { + public void run() { + mAudioTrack.play(); + nativeRunAudioThread(); + } + }); + + // I'd take REALTIME if I could get it! + mAudioThread.setPriority(Thread.MAX_PRIORITY); + mAudioThread.start(); + } + + public static void audioWriteShortBuffer(final short[] buffer) { + for (int i = 0; i < buffer.length;) { + final int result = mAudioTrack.write(buffer, i, buffer.length - i); + if (result > 0) { + i += result; + } else if (result == 0) { + try { + Thread.sleep(1); + } catch (final InterruptedException e) { + // Nom nom + } + } else { + Log.w("SDL", "SDL audio: error return from write(short)"); + return; + } + } + } + + public static void audioWriteByteBuffer(final byte[] buffer) { + for (int i = 0; i < buffer.length;) { + final int result = mAudioTrack.write(buffer, i, buffer.length - i); + if (result > 0) { + i += result; + } else if (result == 0) { + try { + Thread.sleep(1); + } catch (final InterruptedException e) { + // Nom nom + } + } else { + Log.w("SDL", "SDL audio: error return from write(short)"); + return; + } + } + } + + public static void audioQuit() { + if (mAudioThread != null) { + try { + mAudioThread.join(); + } catch (final Exception e) { + Log.v("SDL", "Problem stopping audio thread: " + e); + } + mAudioThread = null; + + // Log.v("SDL", "Finished waiting for audio thread"); + } + + if (mAudioTrack != null) { + mAudioTrack.stop(); + mAudioTrack = null; + } + } } - -/** - Simple nativeInit() runnable -*/ -class SDLMain implements Runnable { - public void run() { - // Runs SDL_main() - SDLActivity.nativeInit(); - - //Log.v("SDL", "SDL thread terminated"); - } -} - - -/** - SDLSurface. This is what we draw on, so we need to know when it's created - in order to do anything useful. - - Because of this, that's where we set up the SDL thread -*/ -class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, - View.OnKeyListener, View.OnTouchListener, SensorEventListener { - - // Sensors - private static SensorManager mSensorManager; - - // Keep track of the surface size to normalize touch events - private static float mWidth, mHeight; - - // Startup - public SDLSurface(Context context) { - super(context); - getHolder().addCallback(this); - - setFocusable(true); - setFocusableInTouchMode(true); - requestFocus(); - setOnKeyListener(this); - setOnTouchListener(this); - - mSensorManager = (SensorManager)context.getSystemService("sensor"); - - // Some arbitrary defaults to avoid a potential division by zero - mWidth = 1.0f; - mHeight = 1.0f; - } - - // Called when we have a valid drawing surface - public void surfaceCreated(SurfaceHolder holder) { - Log.v("SDL", "surfaceCreated()"); - holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); - enableSensor(Sensor.TYPE_ACCELEROMETER, true); - } - - // Called when we lose the surface - public void surfaceDestroyed(SurfaceHolder holder) { - Log.v("SDL", "surfaceDestroyed()"); - if (!SDLActivity.mIsPaused) { - SDLActivity.mIsPaused = true; - SDLActivity.nativePause(); - } - enableSensor(Sensor.TYPE_ACCELEROMETER, false); - } - - // Called when the surface is resized - public void surfaceChanged(SurfaceHolder holder, - int format, int width, int height) { - Log.v("SDL", "surfaceChanged()"); - - int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default - switch (format) { - case PixelFormat.A_8: - Log.v("SDL", "pixel format A_8"); - break; - case PixelFormat.LA_88: - Log.v("SDL", "pixel format LA_88"); - break; - case PixelFormat.L_8: - Log.v("SDL", "pixel format L_8"); - break; - case PixelFormat.RGBA_4444: - Log.v("SDL", "pixel format RGBA_4444"); - sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444 - break; - case PixelFormat.RGBA_5551: - Log.v("SDL", "pixel format RGBA_5551"); - sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551 - break; - case PixelFormat.RGBA_8888: - Log.v("SDL", "pixel format RGBA_8888"); - sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888 - break; - case PixelFormat.RGBX_8888: - Log.v("SDL", "pixel format RGBX_8888"); - sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888 - break; - case PixelFormat.RGB_332: - Log.v("SDL", "pixel format RGB_332"); - sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332 - break; - case PixelFormat.RGB_565: - Log.v("SDL", "pixel format RGB_565"); - sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 - break; - case PixelFormat.RGB_888: - Log.v("SDL", "pixel format RGB_888"); - // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? - sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888 - break; - default: - Log.v("SDL", "pixel format unknown " + format); - break; - } - - mWidth = (float) width; - mHeight = (float) height; - SDLActivity.onNativeResize(width, height, sdlFormat); - Log.v("SDL", "Window size:" + width + "x"+height); - - SDLActivity.startApp(); - } - - // unused - public void onDraw(Canvas canvas) {} - - - - - // Key events - public boolean onKey(View v, int keyCode, KeyEvent event) { - - if (event.getAction() == KeyEvent.ACTION_DOWN) { - //Log.v("SDL", "key down: " + keyCode); - SDLActivity.onNativeKeyDown(keyCode); - return true; - } - else if (event.getAction() == KeyEvent.ACTION_UP) { - //Log.v("SDL", "key up: " + keyCode); - SDLActivity.onNativeKeyUp(keyCode); - return true; - } - - return false; - } - - // Touch events - public boolean onTouch(View v, MotionEvent event) { - { - final int touchDevId = event.getDeviceId(); - final int pointerCount = event.getPointerCount(); - // touchId, pointerId, action, x, y, pressure - int actionPointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent. ACTION_POINTER_ID_SHIFT; /* API 8: event.getActionIndex(); */ - int pointerFingerId = event.getPointerId(actionPointerIndex); - int action = (event.getAction() & MotionEvent.ACTION_MASK); /* API 8: event.getActionMasked(); */ - - float x = event.getX(actionPointerIndex) / mWidth; - float y = event.getY(actionPointerIndex) / mHeight; - float p = event.getPressure(actionPointerIndex); - - if (action == MotionEvent.ACTION_MOVE && pointerCount > 1) { - // TODO send motion to every pointer if its position has - // changed since prev event. - for (int i = 0; i < pointerCount; i++) { - pointerFingerId = event.getPointerId(i); - x = event.getX(i) / mWidth; - y = event.getY(i) / mHeight; - p = event.getPressure(i); - SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); - } - } else { - SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); - } - } - return true; - } - - // Sensor events - public void enableSensor(int sensortype, boolean enabled) { - // TODO: This uses getDefaultSensor - what if we have >1 accels? - if (enabled) { - mSensorManager.registerListener(this, - mSensorManager.getDefaultSensor(sensortype), - SensorManager.SENSOR_DELAY_GAME, null); - } else { - mSensorManager.unregisterListener(this, - mSensorManager.getDefaultSensor(sensortype)); - } - } - - public void onAccuracyChanged(Sensor sensor, int accuracy) { - // TODO - } - - public void onSensorChanged(SensorEvent event) { - if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { - SDLActivity.onNativeAccel(event.values[0] / SensorManager.GRAVITY_EARTH, - event.values[1] / SensorManager.GRAVITY_EARTH, - event.values[2] / SensorManager.GRAVITY_EARTH); - } - } - -} - -/* This is a fake invisible editor view that receives the input and defines the - * pan&scan region - */ -class DummyEdit extends View implements View.OnKeyListener { - InputConnection ic; - - public DummyEdit(Context context) { - super(context); - setFocusableInTouchMode(true); - setFocusable(true); - setOnKeyListener(this); - } - - @Override - public boolean onCheckIsTextEditor() { - return true; - } - - public boolean onKey(View v, int keyCode, KeyEvent event) { - - // This handles the hardware keyboard input - if (event.isPrintingKey()) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1); - } - return true; - } - - if (event.getAction() == KeyEvent.ACTION_DOWN) { - SDLActivity.onNativeKeyDown(keyCode); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - SDLActivity.onNativeKeyUp(keyCode); - return true; - } - - return false; - } - - @Override - public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - ic = new SDLInputConnection(this, true); - - outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI - | 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */; - - return ic; - } -} - -class SDLInputConnection extends BaseInputConnection { - - public SDLInputConnection(View targetView, boolean fullEditor) { - super(targetView, fullEditor); - - } - - @Override - public boolean sendKeyEvent(KeyEvent event) { - - /* - * This handles the keycodes from soft keyboard (and IME-translated - * input from hardkeyboard) - */ - int keyCode = event.getKeyCode(); - if (event.getAction() == KeyEvent.ACTION_DOWN) { - if (event.isPrintingKey()) { - commitText(String.valueOf((char) event.getUnicodeChar()), 1); - } - SDLActivity.onNativeKeyDown(keyCode); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP) { - - SDLActivity.onNativeKeyUp(keyCode); - return true; - } - return super.sendKeyEvent(event); - } - - @Override - public boolean commitText(CharSequence text, int newCursorPosition) { - - nativeCommitText(text.toString(), newCursorPosition); - - return super.commitText(text, newCursorPosition); - } - - @Override - public boolean setComposingText(CharSequence text, int newCursorPosition) { - - nativeSetComposingText(text.toString(), newCursorPosition); - - return super.setComposingText(text, newCursorPosition); - } - - public native void nativeCommitText(String text, int newCursorPosition); - - public native void nativeSetComposingText(String text, int newCursorPosition); - -} diff --git a/android-project/src/org/libsdl/app/SDLInputConnection.java b/android-project/src/org/libsdl/app/SDLInputConnection.java new file mode 100644 --- /dev/null +++ b/android-project/src/org/libsdl/app/SDLInputConnection.java @@ -0,0 +1,47 @@ +package org.libsdl.app; + +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.BaseInputConnection; + +class SDLInputConnection extends BaseInputConnection { + public SDLInputConnection(final View targetView, final boolean fullEditor) { + super(targetView, fullEditor); + } + + @Override + public boolean sendKeyEvent(final KeyEvent event) { + /* + * This handles the keycodes from soft keyboard (and IME-translated + * input from hardkeyboard) + */ + final int keyCode = event.getKeyCode(); + if (event.getAction() == KeyEvent.ACTION_DOWN) { + if (event.isPrintingKey()) { + commitText(String.valueOf((char) event.getUnicodeChar()), 1); + } + SDLActivity.onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + SDLActivity.onNativeKeyUp(keyCode); + return true; + } + return super.sendKeyEvent(event); + } + + @Override + public boolean commitText(final CharSequence text, final int newCursorPosition) { + nativeCommitText(text.toString(), newCursorPosition); + return super.commitText(text, newCursorPosition); + } + + @Override + public boolean setComposingText(final CharSequence text, final int newCursorPosition) { + nativeSetComposingText(text.toString(), newCursorPosition); + return super.setComposingText(text, newCursorPosition); + } + + public native void nativeCommitText(String text, int newCursorPosition); + + public native void nativeSetComposingText(String text, int newCursorPosition); +} \ No newline at end of file diff --git a/android-project/src/org/libsdl/app/SDLMain.java b/android-project/src/org/libsdl/app/SDLMain.java new file mode 100644 --- /dev/null +++ b/android-project/src/org/libsdl/app/SDLMain.java @@ -0,0 +1,12 @@ +package org.libsdl.app; + +/** + * Simple nativeInit() runnable + */ +class SDLMain implements Runnable { + public void run() { + // Runs SDL_main() + SDLActivity.nativeInit(); + // Log.v("SDL", "SDL thread terminated"); + } +} \ No newline at end of file diff --git a/android-project/src/org/libsdl/app/SDLSurface.java b/android-project/src/org/libsdl/app/SDLSurface.java new file mode 100644 --- /dev/null +++ b/android-project/src/org/libsdl/app/SDLSurface.java @@ -0,0 +1,201 @@ +package org.libsdl.app; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.PixelFormat; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; + +/** + * SDLSurface. This is what we draw on, so we need to know when it's created in + * order to do anything useful. + * + * Because of this, that's where we set up the SDL thread + */ +class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, + View.OnKeyListener, View.OnTouchListener, SensorEventListener { + /** Sensors */ + private static SensorManager mSensorManager; + + /** Keep track of the surface size to normalize touch events */ + private static float mWidth, mHeight; + + /** Startup */ + public SDLSurface(final Context context) { + super(context); + getHolder().addCallback(this); + + setFocusable(true); + setFocusableInTouchMode(true); + requestFocus(); + setOnKeyListener(this); + setOnTouchListener(this); + + mSensorManager = (SensorManager) context.getSystemService("sensor"); + + // Some arbitrary defaults to avoid a potential division by zero + mWidth = 1.0f; + mHeight = 1.0f; + } + + /** Called when we have a valid drawing surface */ + public void surfaceCreated(final SurfaceHolder holder) { + Log.v("SDL", "surfaceCreated()"); + holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); + enableSensor(Sensor.TYPE_ACCELEROMETER, true); + } + + /** Called when we lose the surface */ + public void surfaceDestroyed(final SurfaceHolder holder) { + Log.v("SDL", "surfaceDestroyed()"); + if (!SDLActivity.mIsPaused) { + SDLActivity.mIsPaused = true; + SDLActivity.nativePause(); + } + enableSensor(Sensor.TYPE_ACCELEROMETER, false); + } + + /** Called when the surface is resized */ + public void surfaceChanged(final SurfaceHolder holder, final int format, + final int width, final int height) { + Log.v("SDL", "surfaceChanged()"); + + int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default + switch (format) { + case PixelFormat.A_8: + Log.v("SDL", "pixel format A_8"); + break; + case PixelFormat.LA_88: + Log.v("SDL", "pixel format LA_88"); + break; + case PixelFormat.L_8: + Log.v("SDL", "pixel format L_8"); + break; + case PixelFormat.RGBA_4444: + Log.v("SDL", "pixel format RGBA_4444"); + sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444 + break; + case PixelFormat.RGBA_5551: + Log.v("SDL", "pixel format RGBA_5551"); + sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551 + break; + case PixelFormat.RGBA_8888: + Log.v("SDL", "pixel format RGBA_8888"); + sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888 + break; + case PixelFormat.RGBX_8888: + Log.v("SDL", "pixel format RGBX_8888"); + sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888 + break; + case PixelFormat.RGB_332: + Log.v("SDL", "pixel format RGB_332"); + sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332 + break; + case PixelFormat.RGB_565: + Log.v("SDL", "pixel format RGB_565"); + sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 + break; + case PixelFormat.RGB_888: + Log.v("SDL", "pixel format RGB_888"); + // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? + sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888 + break; + default: + Log.v("SDL", "pixel format unknown " + format); + break; + } + + mWidth = (float) width; + mHeight = (float) height; + SDLActivity.onNativeResize(width, height, sdlFormat); + Log.v("SDL", "Window size:" + width + "x" + height); + + SDLActivity.startApp(); + } + + // unused + public void onDraw(final Canvas canvas) { + } + + /** Key events */ + public boolean onKey(final View v, final int keyCode, final KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + // Log.v("SDL", "key down: " + keyCode); + SDLActivity.onNativeKeyDown(keyCode); + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + // Log.v("SDL", "key up: " + keyCode); + SDLActivity.onNativeKeyUp(keyCode); + return true; + } + + return false; + } + + /** Touch events */ + public boolean onTouch(final View v, final MotionEvent event) { + final int touchDevId = event.getDeviceId(); + final int pointerCount = event.getPointerCount(); + // touchId, pointerId, action, x, y, pressure + // API: event.getActionIndex(); + final int actionPointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT; + int pointerFingerId = event.getPointerId(actionPointerIndex); + // API 8: event.getActionMasked(); + final int action = (event.getAction() & MotionEvent.ACTION_MASK); + + float x = event.getX(actionPointerIndex) / mWidth; + float y = event.getY(actionPointerIndex) / mHeight; + float p = event.getPressure(actionPointerIndex); + + if (action == MotionEvent.ACTION_MOVE && pointerCount > 1) { + // TODO send motion to every pointer if its position has + // changed since prev event. + for (int i = 0; i < pointerCount; i++) { + pointerFingerId = event.getPointerId(i); + x = event.getX(i) / mWidth; + y = event.getY(i) / mHeight; + p = event.getPressure(i); + SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, + x, y, p); + } + } else { + SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, + y, p); + } + return true; + } + + /** Sensor events */ + public void enableSensor(final int sensortype, final boolean enabled) { + // TODO: This uses getDefaultSensor - what if we have >1 accels? + if (enabled) { + mSensorManager.registerListener(this, + mSensorManager.getDefaultSensor(sensortype), + SensorManager.SENSOR_DELAY_GAME, null); + } else { + mSensorManager.unregisterListener(this, + mSensorManager.getDefaultSensor(sensortype)); + } + } + + public void onAccuracyChanged(final Sensor sensor, final int accuracy) { + // TODO + } + + public void onSensorChanged(final SensorEvent event) { + if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { + SDLActivity.onNativeAccel(event.values[0] + / SensorManager.GRAVITY_EARTH, event.values[1] + / SensorManager.GRAVITY_EARTH, event.values[2] + / SensorManager.GRAVITY_EARTH); + } + } +} \ No newline at end of file diff --git a/android-project/src/org/libsdl/app/ShowTextInputHandler.java b/android-project/src/org/libsdl/app/ShowTextInputHandler.java new file mode 100644 --- /dev/null +++ b/android-project/src/org/libsdl/app/ShowTextInputHandler.java @@ -0,0 +1,44 @@ +package org.libsdl.app; + +import android.content.Context; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.AbsoluteLayout; + +class ShowTextInputHandler implements Runnable { + /** + * This is used to regulate the pan&scan method to have some offset from the + * bottom edge of the input region and the top edge of an input method (soft + * keyboard) + */ + static final int HEIGHT_PADDING = 15; + public int x, y, w, h; + + public ShowTextInputHandler(final int x, final int y, final int w, + final int h) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + } + + public void run() { + final AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams( + w, h + HEIGHT_PADDING, x, y); + + if (SDLActivity.mTextEdit == null) { + SDLActivity.mTextEdit = new DummyEdit(SDLActivity.getContext()); + + SDLActivity.mLayout.addView(SDLActivity.mTextEdit, params); + } else { + SDLActivity.mTextEdit.setLayoutParams(params); + } + + SDLActivity.mTextEdit.setVisibility(View.VISIBLE); + SDLActivity.mTextEdit.requestFocus(); + + final InputMethodManager imm = (InputMethodManager) SDLActivity + .getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(SDLActivity.mTextEdit, 0); + } +} \ No newline at end of file