Browse Source

Fixed bug 2266 - please add notifications for clipboard updates on Android

Sylvain

Hi! here's a patch for that with two class loaded regarding API level.
Test both case : before API 11 and after.

I also remove now unused GetSystemServiceFromUIThread() and minor clean-up (haptic warning prototype).
Sam Lantinga 7 năm trước cách đây
mục cha
commit
fe21a74763
2 tập tin đã thay đổi với 169 bổ sung132 xóa
  1. 117 29
      android-project/src/org/libsdl/app/SDLActivity.java
  2. 52 103
      src/core/android/SDL_android.c

+ 117 - 29
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;
+    }
+}
+
+

+ 52 - 103
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