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 2017-08-27 18:43:52 -07:00
parent 6885bc88bf
commit fe21a74763
2 changed files with 169 additions and 132 deletions

View File

@ -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;
}
}

View File

@ -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