SDL: add SDL_AndroidShowToast for https://developer.android.com/reference/android/widget/Toast

From 1a924bc0bb15a7aaf4fdfc1628f892bbc4717587 Mon Sep 17 00:00:00 2001
From: Amir <[EMAIL REDACTED]>
Date: Fri, 19 Feb 2021 12:54:57 +0300
Subject: [PATCH] add SDL_AndroidShowToast for
 https://developer.android.com/reference/android/widget/Toast

---
 .../main/java/org/libsdl/app/SDLActivity.java | 47 +++++++++++++++++++
 include/SDL_system.h                          | 16 +++++++
 src/core/android/SDL_android.c                | 19 ++++++++
 src/core/android/SDL_android.h                |  3 ++
 src/dynapi/SDL_dynapi_overrides.h             |  1 +
 src/dynapi/SDL_dynapi_procs.h                 |  1 +
 6 files changed, 87 insertions(+)

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 e7e5a2ac7..ff8ed76f6 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
@@ -52,6 +52,7 @@
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import java.util.Hashtable;
 import java.util.Locale;
@@ -1630,6 +1631,52 @@ public static int openURL(String url)
         }
         return 0;
     }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static int showToast(String message, int duration, int gravity, int xOffset, int yOffset)
+    {
+        if(null == mSingleton) {
+            return - 1;
+        }
+
+        try
+        {
+            class OneShotTask implements Runnable {
+                String mMessage;
+                int mDuration;
+                int mGravity;
+                int mXOffset;
+                int mYOffset;
+
+                OneShotTask(String message, int duration, int gravity, int xOffset, int yOffset) {
+                    mMessage  = message;
+                    mDuration = duration;
+                    mGravity  = gravity;
+                    mXOffset  = xOffset;
+                    mYOffset  = yOffset;
+                }
+
+                public void run() {
+                    try
+                    {
+                        Toast toast = Toast.makeText(mSingleton, mMessage, mDuration);
+                        if (mGravity >= 0) {
+                            toast.setGravity(mGravity, mXOffset, mYOffset);
+                        }
+                        toast.show();
+                    } catch(Exception ex) {
+                        Log.e(TAG, ex.getMessage());
+                    }
+                }
+            }
+            mSingleton.runOnUiThread(new OneShotTask(message, duration, gravity, xOffset, yOffset));
+        } catch(Exception ex) {
+            return -1;
+        }
+        return 0;
+    }
 }
 
 /**
diff --git a/include/SDL_system.h b/include/SDL_system.h
index 06ac41cd3..f0134417d 100644
--- a/include/SDL_system.h
+++ b/include/SDL_system.h
@@ -215,6 +215,22 @@ extern DECLSPEC const char * SDLCALL SDL_AndroidGetExternalStoragePath(void);
  */
 extern DECLSPEC SDL_bool SDLCALL SDL_AndroidRequestPermission(const char *permission);
 
+/**
+   \brief Show android toast notification
+
+    Show toast in UI thread [https://developer.android.com/guide/topics/ui/notifiers/toasts]
+    Params description
+    message   : text message to be shown
+    duration  : 0 - short [https://developer.android.com/reference/android/widget/Toast#LENGTH_SHORT],
+                1 - long  [https://developer.android.com/reference/android/widget/Toast#LENGTH_LONG]
+    gravity   : the location at which the notification should appear on the screen.
+                It's an optional parameter. Set -1 if you don't want specify any gravity or
+                choose some value from https://developer.android.com/reference/android/view/Gravity
+    xOffset   : set this parameter only when gravity >=0
+    yOffset   : set this parameter only when gravity >=0
+*/
+extern DECLSPEC int SDLCALL SDL_AndroidShowToast(const char* message, int duration, int gravity, int xOffset, int yOffset);
+
 #endif /* __ANDROID__ */
 
 /* Platform specific functions for WinRT */
diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index 5831ea99b..a01787ea3 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -312,6 +312,7 @@ static jmethodID midManualBackButton;
 static jmethodID midMinimizeWindow;
 static jmethodID midOpenURL;
 static jmethodID midRequestPermission;
+static jmethodID midShowToast;
 static jmethodID midSendMessage;
 static jmethodID midSetActivityTitle;
 static jmethodID midSetCustomCursor;
@@ -591,6 +592,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
     midMinimizeWindow = (*env)->GetStaticMethodID(env, mActivityClass, "minimizeWindow","()V");
     midOpenURL = (*env)->GetStaticMethodID(env, mActivityClass, "openURL", "(Ljava/lang/String;)I");
     midRequestPermission = (*env)->GetStaticMethodID(env, mActivityClass, "requestPermission", "(Ljava/lang/String;I)V");
+    midShowToast = (*env)->GetStaticMethodID(env, mActivityClass, "showToast", "(Ljava/lang/String;IIII)I");
     midSendMessage = (*env)->GetStaticMethodID(env, mActivityClass, "sendMessage", "(II)Z");
     midSetActivityTitle = (*env)->GetStaticMethodID(env, mActivityClass, "setActivityTitle","(Ljava/lang/String;)Z");
     midSetCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "setCustomCursor", "(I)Z");
@@ -621,6 +623,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
         !midMinimizeWindow ||
         !midOpenURL ||
         !midRequestPermission ||
+        !midShowToast ||
         !midSendMessage ||
         !midSetActivityTitle ||
         !midSetCustomCursor ||
@@ -2468,6 +2471,11 @@ SDL_bool SDL_AndroidRequestPermission(const char *permission)
     return Android_JNI_RequestPermission(permission);
 }
 
+int SDL_AndroidShowToast(const char* message, int duration, int gravity, int xOffset, int yOffset)
+{
+    return Android_JNI_ShowToast(message, duration, gravity, xOffset, yOffset);
+}
+
 void Android_JNI_GetManifestEnvironmentVariables(void)
 {
     if (!mActivityClass || !midGetManifestEnvironmentVariables) {
@@ -2547,6 +2555,17 @@ SDL_bool Android_JNI_RequestPermission(const char *permission)
 	return bPermissionRequestResult;
 }
 
+/* Show toast notification */
+int Android_JNI_ShowToast(const char* message, int duration, int gravity, int xOffset, int yOffset)
+{
+    int result = 0;
+    JNIEnv *env = Android_JNI_GetEnv();
+    jstring jmessage = (*env)->NewStringUTF(env, message);
+    result = (*env)->CallStaticIntMethod(env, mActivityClass, midShowToast, jmessage, duration, gravity, xOffset, yOffset);
+    (*env)->DeleteLocalRef(env, jmessage);
+    return result;
+}
+
 int Android_JNI_GetLocale(char *buf, size_t buflen)
 {
     AConfiguration *cfg;
diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h
index 0fb07d615..7c4e3dbad 100644
--- a/src/core/android/SDL_android.h
+++ b/src/core/android/SDL_android.h
@@ -129,6 +129,9 @@ SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled);
 /* Request permission */
 SDL_bool Android_JNI_RequestPermission(const char *permission);
 
+/* Show toast notification */
+int Android_JNI_ShowToast(const char* message, int duration, int gravity, int xOffset, int yOffset);
+
 int Android_JNI_OpenURL(const char *url);
 
 int SDL_GetAndroidSDKVersion(void);
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 4d698c60b..e32cd1fe6 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -766,6 +766,7 @@
 #define SDL_GetPreferredLocales SDL_GetPreferredLocales_REAL
 #define SDL_SIMDRealloc SDL_SIMDRealloc_REAL
 #define SDL_AndroidRequestPermission SDL_AndroidRequestPermission_REAL
+#define SDL_AndroidShowToast SDL_AndroidShowToast_REAL
 #define SDL_OpenURL SDL_OpenURL_REAL
 #define SDL_HasSurfaceRLE SDL_HasSurfaceRLE_REAL
 #define SDL_GameControllerHasLED SDL_GameControllerHasLED_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 8e67893a2..61437c8d0 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -826,6 +826,7 @@ SDL_DYNAPI_PROC(SDL_Locale *,SDL_GetPreferredLocales,(void),(),return)
 SDL_DYNAPI_PROC(void*,SDL_SIMDRealloc,(void *a, const size_t b),(a, b),return)
 #ifdef __ANDROID__
 SDL_DYNAPI_PROC(SDL_bool,SDL_AndroidRequestPermission,(const char *a),(a),return)
+SDL_DYNAPI_PROC(int,SDL_AndroidShowToast,(const char* message, int duration, int gravity, int xOffset, int yOffset),(message,duration,gravity,xOffset,yOffset),return)
 #endif
 SDL_DYNAPI_PROC(int,SDL_OpenURL,(const char *a),(a),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_HasSurfaceRLE,(SDL_Surface *a),(a),return)