diff --git a/android-project/src/org/libsdl/app/SDLActivity.java b/android-project/src/org/libsdl/app/SDLActivity.java index a130c0461..7420714df 100644 --- a/android-project/src/org/libsdl/app/SDLActivity.java +++ b/android-project/src/org/libsdl/app/SDLActivity.java @@ -63,6 +63,8 @@ public class SDLActivity extends Activity { protected static ViewGroup mLayout; protected static SDLJoystickHandler mJoystickHandler; protected static SDLHapticHandler mHapticHandler; + protected static SDLClipboardHandler mClipboardHandler; + // This is what SDL runs in. It invokes SDL_main(), eventually protected static Thread mSDLThread; @@ -116,6 +118,7 @@ public class SDLActivity extends Activity { mLayout = null; mJoystickHandler = null; mHapticHandler = null; + mClipboardHandler = null; mSDLThread = null; mAudioTrack = null; mAudioRecord = null; @@ -187,6 +190,13 @@ public class SDLActivity extends Activity { } mHapticHandler = new SDLHapticHandler(); + if (Build.VERSION.SDK_INT >= 11) { + mClipboardHandler = new SDLClipboardHandler_API11(); + } else { + /* Before API 11, no clipboard notification (eg no SDL_CLIPBOARDUPDATE) */ + mClipboardHandler = new SDLClipboardHandler_Old(); + } + mLayout = new RelativeLayout(this); mLayout.addView(mSurface); @@ -498,6 +508,7 @@ public class SDLActivity extends Activity { int action, float x, float y, float p); public static native void onNativeAccel(float x, float y, float z); + public static native void onNativeClipboardChanged(); public static native void onNativeSurfaceChanged(); public static native void onNativeSurfaceDestroyed(); public static native int nativeAddJoystick(int device_id, String name, @@ -607,35 +618,6 @@ public class SDLActivity extends Activity { return mSingleton; } - /** - * This method is called by SDL using JNI. - * @return result of getSystemService(name) but executed on UI thread. - */ - public Object getSystemServiceFromUiThread(final String name) { - final Object lock = new Object(); - final Object[] results = new Object[2]; // array for writable variables - synchronized (lock) { - runOnUiThread(new Runnable() { - @Override - public void run() { - synchronized (lock) { - results[0] = getSystemService(name); - results[1] = Boolean.TRUE; - lock.notify(); - } - } - }); - if (results[1] == null) { - try { - lock.wait(); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } - } - } - return results[0]; - } - static class ShowTextInputTask implements Runnable { /* * This is used to regulate the pan&scan method to have some offset from @@ -1182,6 +1164,29 @@ public class SDLActivity extends Activity { return dialog; } + + /** + * This method is called by SDL using JNI. + */ + public static boolean clipboardHasText() { + return mClipboardHandler.clipboardHasText(); + } + + /** + * This method is called by SDL using JNI. + */ + public static String clipboardGetText() { + return mClipboardHandler.clipboardGetText(); + } + + /** + * This method is called by SDL using JNI. + */ + public static void clipboardSetText(String string) { + mClipboardHandler.clipboardSetText(string); + return; + } + } /** @@ -1993,3 +1998,86 @@ class SDLHapticHandler { return null; } } + + +interface SDLClipboardHandler { + + public boolean clipboardHasText(); + public String clipboardGetText(); + public void clipboardSetText(String string); + +} + + +class SDLClipboardHandler_API11 implements + SDLClipboardHandler, + android.content.ClipboardManager.OnPrimaryClipChangedListener { + + protected android.content.ClipboardManager mClipMgr; + + SDLClipboardHandler_API11() { + mClipMgr = (android.content.ClipboardManager) SDLActivity.mSingleton.getContext().getSystemService(Context.CLIPBOARD_SERVICE); + mClipMgr.addPrimaryClipChangedListener(this); + } + + @Override + public boolean clipboardHasText() { + return mClipMgr.hasText(); + } + + @Override + public String clipboardGetText() { + CharSequence text; + text = mClipMgr.getText(); + if (text != null) { + return text.toString(); + } + return null; + } + + @Override + public void clipboardSetText(String string) { + mClipMgr.removePrimaryClipChangedListener(this); + mClipMgr.setText(string); + mClipMgr.addPrimaryClipChangedListener(this); + } + + @Override + public void onPrimaryClipChanged() { + SDLActivity.onNativeClipboardChanged(); + } + +} + +class SDLClipboardHandler_Old implements + SDLClipboardHandler { + + protected android.text.ClipboardManager mClipMgrOld; + + SDLClipboardHandler_Old() { + mClipMgrOld = (android.text.ClipboardManager) SDLActivity.mSingleton.getContext().getSystemService(Context.CLIPBOARD_SERVICE); + } + + @Override + public boolean clipboardHasText() { + return mClipMgrOld.hasText(); + } + + @Override + public String clipboardGetText() { + CharSequence text; + text = mClipMgrOld.getText(); + if (text != null) { + return text.toString(); + } + return null; + } + + @Override + public void clipboardSetText(String string) { + mClipMgrOld.setText(string); + return; + } +} + + diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index 784a570d7..6dd078f32 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -91,6 +91,14 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveJoystick)( JNIEnv* env, jclass jcls, jint device_id); +JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddHaptic)( + JNIEnv* env, jclass jcls, + jint device_id, jstring device_name); + +JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveHaptic)( + JNIEnv* env, jclass jcls, + jint device_id); + JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)( JNIEnv* env, jclass jcls); @@ -121,6 +129,9 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)( JNIEnv* env, jclass jcls, jfloat x, jfloat y, jfloat z); +JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)( + JNIEnv* env, jclass jcls); + JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)( JNIEnv* env, jclass cls); @@ -187,7 +198,10 @@ static jmethodID midInputGetInputDeviceIds; static jmethodID midSendMessage; static jmethodID midShowTextInput; static jmethodID midIsScreenKeyboardShown; -static jmethodID midGetSystemServiceFromUiThread; +static jmethodID midClipboardSetText; +static jmethodID midClipboardGetText; +static jmethodID midClipboardHasText; + /* static fields */ static jfieldID fidSeparateMouseAndTouch; @@ -269,8 +283,12 @@ JNIEXPORT void JNICALL SDL_Android_Init(JNIEnv* mEnv, jclass cls) "showTextInput", "(IIII)Z"); midIsScreenKeyboardShown = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "isScreenKeyboardShown","()Z"); - midGetSystemServiceFromUiThread = (*mEnv)->GetMethodID(mEnv, mActivityClass, - "getSystemServiceFromUiThread", "(Ljava/lang/String;)Ljava/lang/Object;"); + midClipboardSetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, + "clipboardSetText", "(Ljava/lang/String;)V"); + midClipboardGetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, + "clipboardGetText", "()Ljava/lang/String;"); + midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, + "clipboardHasText", "()Z"); bHasNewData = SDL_FALSE; @@ -279,7 +297,8 @@ JNIEXPORT void JNICALL SDL_Android_Init(JNIEnv* mEnv, jclass cls) !midCaptureOpen || !midCaptureReadShortBuffer || !midCaptureReadByteBuffer || !midCaptureClose || !midPollInputDevices || !midPollHapticDevices || !midHapticRun || !midSetActivityTitle || !midSetOrientation || !midGetContext || !midInputGetInputDeviceIds || - !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown || !midGetSystemServiceFromUiThread) { + !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown || + !midClipboardSetText || !midClipboardGetText || !midClipboardHasText) { __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly"); } @@ -366,7 +385,7 @@ JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveJoystick)( return Android_RemoveJoystick(device_id); } -JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeAddHaptic( +JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeAddHaptic)( JNIEnv* env, jclass jcls, jint device_id, jstring device_name) { int retval; @@ -379,7 +398,7 @@ JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeAddHaptic( return retval; } -JNIEXPORT jint JNICALL Java_org_libsdl_app_SDLActivity_nativeRemoveHaptic( +JNIEXPORT jint JNICALL SDL_JAVA_INTERFACE(nativeRemoveHaptic)( JNIEnv* env, jclass jcls, jint device_id) { return Android_RemoveHaptic(device_id); @@ -492,6 +511,13 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeAccel)( bHasNewData = SDL_TRUE; } +/* Clipboard */ +JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeClipboardChanged)( + JNIEnv* env, jclass jcls) +{ + SDL_SendClipboardUpdate(); +} + /* Low memory */ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeLowMemory)( JNIEnv* env, jclass cls) @@ -1363,118 +1389,41 @@ int Android_JNI_FileClose(SDL_RWops* ctx) return Internal_Android_JNI_FileClose(ctx, SDL_TRUE); } -/* returns a new global reference which needs to be released later */ -static jobject Android_JNI_GetSystemServiceObject(const char* name) -{ - struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); - JNIEnv* env = Android_JNI_GetEnv(); - jobject retval = NULL; - jstring service; - jobject context; - jobject manager; - - if (!LocalReferenceHolder_Init(&refs, env)) { - LocalReferenceHolder_Cleanup(&refs); - return NULL; - } - - service = (*env)->NewStringUTF(env, name); - - /* context = SDLActivity.getContext(); */ - context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext); - - manager = (*env)->CallObjectMethod(env, context, midGetSystemServiceFromUiThread, service); - - (*env)->DeleteLocalRef(env, service); - - retval = manager ? (*env)->NewGlobalRef(env, manager) : NULL; - LocalReferenceHolder_Cleanup(&refs); - return retval; -} - -#define SETUP_CLIPBOARD(error) \ - struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); \ - JNIEnv* env = Android_JNI_GetEnv(); \ - jobject clipboard; \ - if (!LocalReferenceHolder_Init(&refs, env)) { \ - LocalReferenceHolder_Cleanup(&refs); \ - return error; \ - } \ - clipboard = Android_JNI_GetSystemServiceObject("clipboard"); \ - if (!clipboard) { \ - LocalReferenceHolder_Cleanup(&refs); \ - return error; \ - } - -#define CLEANUP_CLIPBOARD() \ - LocalReferenceHolder_Cleanup(&refs); - int Android_JNI_SetClipboardText(const char* text) { - /* Watch out for C89 scoping rules because of the macro */ - SETUP_CLIPBOARD(-1) - - /* Nest the following in a scope to avoid C89 declaration rules triggered by the macro */ - { - jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "setText", "(Ljava/lang/CharSequence;)V"); - jstring string = (*env)->NewStringUTF(env, text); - (*env)->CallVoidMethod(env, clipboard, mid, string); - (*env)->DeleteGlobalRef(env, clipboard); - (*env)->DeleteLocalRef(env, string); - } - CLEANUP_CLIPBOARD(); - + JNIEnv* env = Android_JNI_GetEnv(); + jstring string = (*env)->NewStringUTF(env, text); + (*env)->CallStaticVoidMethod(env, mActivityClass, midClipboardSetText, string); + (*env)->DeleteLocalRef(env, string); return 0; } char* Android_JNI_GetClipboardText(void) { - /* Watch out for C89 scoping rules because of the macro */ - SETUP_CLIPBOARD(SDL_strdup("")) - - /* Nest the following in a scope to avoid C89 declaration rules triggered by the macro */ - { - jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "getText", "()Ljava/lang/CharSequence;"); - jobject sequence = (*env)->CallObjectMethod(env, clipboard, mid); - (*env)->DeleteGlobalRef(env, clipboard); - if (sequence) { - jstring string; - const char* utf; - mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, sequence), "toString", "()Ljava/lang/String;"); - string = (jstring)((*env)->CallObjectMethod(env, sequence, mid)); - utf = (*env)->GetStringUTFChars(env, string, 0); - if (utf) { - char* text = SDL_strdup(utf); - (*env)->ReleaseStringUTFChars(env, string, utf); - - CLEANUP_CLIPBOARD(); - - return text; - } + JNIEnv* env = Android_JNI_GetEnv(); + char* text = NULL; + jstring string; + + string = (*env)->CallStaticObjectMethod(env, mActivityClass, midClipboardGetText); + if (string) { + const char* utf = (*env)->GetStringUTFChars(env, string, 0); + if (utf) { + text = SDL_strdup(utf); + (*env)->ReleaseStringUTFChars(env, string, utf); } + (*env)->DeleteLocalRef(env, string); } - CLEANUP_CLIPBOARD(); - - return SDL_strdup(""); + + return (text == NULL) ? SDL_strdup("") : text; } SDL_bool Android_JNI_HasClipboardText(void) { - jmethodID mid; - jboolean has; - /* Watch out for C89 scoping rules because of the macro */ - SETUP_CLIPBOARD(SDL_FALSE) - - mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, clipboard), "hasText", "()Z"); - has = (*env)->CallBooleanMethod(env, clipboard, mid); - (*env)->DeleteGlobalRef(env, clipboard); - - CLEANUP_CLIPBOARD(); - - return has ? SDL_TRUE : SDL_FALSE; + JNIEnv* env = Android_JNI_GetEnv(); + jboolean retval = (*env)->CallStaticBooleanMethod(env, mActivityClass, midClipboardHasText); + return (retval == JNI_TRUE) ? SDL_TRUE : SDL_FALSE; } - /* returns 0 on success or -1 on error (others undefined then) * returns truthy or falsy value in plugged, charged and battery * returns the value in seconds and percent or -1 if not available