Commit f536fbea7162d939689e1d9899d082896dd67c4a

Sam Lantinga 2018-03-16T11:08:53

Reimplemented Android cursor API support using reflection so it builds with older SDKs

diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
index e665c41..3ea99da 100644
--- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
@@ -77,10 +77,8 @@ public class SDLActivity extends Activity {
     protected static boolean mScreenKeyboardShown;
     protected static ViewGroup mLayout;
     protected static SDLClipboardHandler mClipboardHandler;
-    //#CURSORIMPLEENTATION
-    //protected static Hashtable<Integer, PointerIcon> mCursors;
-    //protected static int mLastCursorID;
-    //protected static PointerIcon mActiveCursor;
+    protected static Hashtable<Integer, Object> mCursors;
+    protected static int mLastCursorID;
 
 
     // This is what SDL runs in. It invokes SDL_main(), eventually
@@ -153,10 +151,8 @@ public class SDLActivity extends Activity {
         mTextEdit = null;
         mLayout = null;
         mClipboardHandler = null;
-        //#CURSORIMPLEENTATION
-        //mCursors = new Hashtable<Integer, PointerIcon>();
-        //mLastCursorID = 0;
-        //mActiveCursor = null;
+        mCursors = new Hashtable<Integer, Object>();
+        mLastCursorID = 0;
         mSDLThread = null;
         mExitCalledFromJava = false;
         mBrokenLibraries = false;
@@ -1093,69 +1089,91 @@ public class SDLActivity extends Activity {
     /**
      * This method is called by SDL using JNI.
      */
-    /**
-     * #CURSORIMPLEENTATION
-     * The cursor implementation requires API 24 or above
-     *
     public static int createCustomCursor(int[] colors, int width, int height, int hotSpotX, int hotSpotY) {
         Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
         ++mLastCursorID;
-        mCursors.put(mLastCursorID, PointerIcon.create(bitmap, hotSpotX, hotSpotY));
+        // This requires API 24, so use reflection to implement this
+        try {
+            Class PointerIconClass = Class.forName("android.view.PointerIcon");
+            Class[] arg_types = new Class[] { Bitmap.class, float.class, float.class };
+            Method create = PointerIconClass.getMethod("create", arg_types);
+            mCursors.put(mLastCursorID, create.invoke(null, bitmap, hotSpotX, hotSpotY));
+        } catch (Exception e) {
+            return 0;
+        }
         return mLastCursorID;
     }
 
-    public static void setCustomCursor(int cursorID) {
-        mActiveCursor = mCursors.get(cursorID);
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setCustomCursor(int cursorID) {
+        // This requires API 24, so use reflection to implement this
+        try {
+            Class PointerIconClass = Class.forName("android.view.PointerIcon");
+            Method setPointerIcon = SDLSurface.class.getMethod("setPointerIcon", PointerIconClass);
+            setPointerIcon.invoke(mSurface, mCursors.get(cursorID));
+        } catch (Exception e) {
+            return false;
+        }
+        return true;
     }
 
-    public static void setSystemCursor(int cursorID) {
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setSystemCursor(int cursorID) {
+        int cursor_type = 0; //PointerIcon.TYPE_NULL;
         switch (cursorID) {
-        case SDL_SYSTEM_CURSOR_NONE:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_NULL);
-            break;
         case SDL_SYSTEM_CURSOR_ARROW:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_ARROW);
+            cursor_type = 1000; //PointerIcon.TYPE_ARROW;
             break;
         case SDL_SYSTEM_CURSOR_IBEAM:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_TEXT);
+            cursor_type = 1008; //PointerIcon.TYPE_TEXT;
             break;
         case SDL_SYSTEM_CURSOR_WAIT:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_WAIT);
+            cursor_type = 1004; //PointerIcon.TYPE_WAIT;
             break;
         case SDL_SYSTEM_CURSOR_CROSSHAIR:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_CROSSHAIR);
+            cursor_type = 1007; //PointerIcon.TYPE_CROSSHAIR;
             break;
         case SDL_SYSTEM_CURSOR_WAITARROW:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_WAIT);
+            cursor_type = 1004; //PointerIcon.TYPE_WAIT;
             break;
         case SDL_SYSTEM_CURSOR_SIZENWSE:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW);
+            cursor_type = 1017; //PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
             break;
         case SDL_SYSTEM_CURSOR_SIZENESW:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW);
+            cursor_type = 1016; //PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
             break;
         case SDL_SYSTEM_CURSOR_SIZEWE:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW);
+            cursor_type = 1014; //PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
             break;
         case SDL_SYSTEM_CURSOR_SIZENS:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW);
+            cursor_type = 1015; //PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
             break;
         case SDL_SYSTEM_CURSOR_SIZEALL:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_GRAB);
+            cursor_type = 1020; //PointerIcon.TYPE_GRAB;
             break;
         case SDL_SYSTEM_CURSOR_NO:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_NO_DROP);
+            cursor_type = 1012; //PointerIcon.TYPE_NO_DROP;
             break;
         case SDL_SYSTEM_CURSOR_HAND:
-            mActiveCursor = PointerIcon.getSystemIcon(SDL.getContext(), PointerIcon.TYPE_HAND);
+            cursor_type = 1002; //PointerIcon.TYPE_HAND;
             break;
         }
+        // This requires API 24, so use reflection to implement this
+        try {
+            Class PointerIconClass = Class.forName("android.view.PointerIcon");
+            Class[] arg_types = new Class[] { Context.class, int.class };
+            Method getSystemIcon = PointerIconClass.getMethod("getSystemIcon", arg_types);
+            Method setPointerIcon = SDLSurface.class.getMethod("setPointerIcon", PointerIconClass);
+            setPointerIcon.invoke(mSurface, getSystemIcon.invoke(null, SDL.getContext(), cursor_type));
+        } catch (Exception e) {
+            return false;
+        }
+        return true;
     }
-
-    public static PointerIcon getCursor() {
-        return mActiveCursor;
-    }
-    */
 }
 
 /**
@@ -1547,14 +1565,6 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
                                       event.values[2] / SensorManager.GRAVITY_EARTH);
         }
     }
-
-    /**
-     * #CURSORIMPLEENTATION
-    @Override
-    public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
-        return SDLActivity.getCursor();
-    }
-    */
 }
 
 /* This is a fake invisible editor view that receives the input and defines the
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index dd7f1ba..c5bec90 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -335,17 +335,16 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv* mEnv, jclass c
                                 "getManifestEnvironmentVariables", "()Z");
 
     midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");
-
-    /* Custom cursor implementation is only available on API 24 and above */
     midCreateCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "createCustomCursor", "([IIIII)I");
-    midSetCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setCustomCursor", "(I)V");
-    midSetSystemCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setSystemCursor", "(I)V");
+    midSetCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setCustomCursor", "(I)Z");
+    midSetSystemCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setSystemCursor", "(I)Z");
 
     if (!midGetNativeSurface ||
        !midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsAndroidTV || !midInputGetInputDeviceIds ||
        !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||
        !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||
-       !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables|| !midGetDisplayDPI) {
+       !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||
+       !midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor) {
         __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");
     }
 
@@ -2178,34 +2177,29 @@ int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y)
 {
     JNIEnv *mEnv = Android_JNI_GetEnv();
     int custom_cursor = 0;
-    if (midCreateCustomCursor) {
-        jintArray pixels;
-        pixels = (*mEnv)->NewIntArray(mEnv, surface->w * surface->h);
-        if (!pixels) {
-            return 0;
-        }
+    jintArray pixels;
+    pixels = (*mEnv)->NewIntArray(mEnv, surface->w * surface->h);
+    if (pixels) {
         (*mEnv)->SetIntArrayRegion(mEnv, pixels, 0, surface->w * surface->h, (int *)surface->pixels);
         custom_cursor = (*mEnv)->CallStaticIntMethod(mEnv, mActivityClass, midCreateCustomCursor, pixels, surface->w, surface->h, hot_x, hot_y);
         (*mEnv)->DeleteLocalRef(mEnv, pixels);
+    } else {
+        SDL_OutOfMemory();
     }
     return custom_cursor;
 }
 
 
-void Android_JNI_SetCustomCursor(int cursorID)
+SDL_bool Android_JNI_SetCustomCursor(int cursorID)
 {
     JNIEnv *mEnv = Android_JNI_GetEnv();
-    if (midSetCustomCursor) {
-        (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetCustomCursor, cursorID);
-    }
+    return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetCustomCursor, cursorID);
 }
 
-void Android_JNI_SetSystemCursor(int cursorID)
+SDL_bool Android_JNI_SetSystemCursor(int cursorID)
 {
     JNIEnv *mEnv = Android_JNI_GetEnv();
-    if (midSetSystemCursor) {
-        (*mEnv)->CallStaticVoidMethod(mEnv, mActivityClass, midSetSystemCursor, cursorID);
-    }
+    return (*mEnv)->CallStaticBooleanMethod(mEnv, mActivityClass, midSetSystemCursor, cursorID);
 }
 
 #endif /* __ANDROID__ */
diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h
index b801092..5eb727d 100644
--- a/src/core/android/SDL_android.h
+++ b/src/core/android/SDL_android.h
@@ -104,8 +104,8 @@ int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *bu
 
 /* Cursor support */
 int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y);
-void Android_JNI_SetCustomCursor(int cursorID);
-void Android_JNI_SetSystemCursor(int cursorID);
+SDL_bool Android_JNI_SetCustomCursor(int cursorID);
+SDL_bool Android_JNI_SetSystemCursor(int cursorID);
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
diff --git a/src/video/android/SDL_androidmouse.c b/src/video/android/SDL_androidmouse.c
index b84b6ce..13c6271 100644
--- a/src/video/android/SDL_androidmouse.c
+++ b/src/video/android/SDL_androidmouse.c
@@ -121,12 +121,18 @@ Android_ShowCursor(SDL_Cursor * cursor)
     if (cursor) {
         SDL_AndroidCursorData *data = (SDL_AndroidCursorData*)cursor->driverdata;
         if (data->custom_cursor) {
-            Android_JNI_SetCustomCursor(data->custom_cursor);
+            if (!Android_JNI_SetCustomCursor(data->custom_cursor)) {
+                return SDL_Unsupported();
+            }
         } else {
-            Android_JNI_SetSystemCursor(data->system_cursor);
+            if (!Android_JNI_SetSystemCursor(data->system_cursor)) {
+                return SDL_Unsupported();
+            }
         }
     } else {
-        Android_JNI_SetSystemCursor(-1);
+        if (!Android_JNI_SetSystemCursor(-1)) {
+            return SDL_Unsupported();
+        }
     }
     return 0;
 }