Browse Source

android: Fix assorted compatibility issues with plyer/pyjnius

pyjnius needs to do env->FindClass in the app thread, which only works if the app thread was created by Java, so I changed android_native_app_glue to do that instead of via pthreads
rdb 1 week ago
parent
commit
ea55924e59

+ 2 - 0
makepanda/makepanda.py

@@ -4948,6 +4948,7 @@ if GetTarget() == 'android':
     TargetAdd('org/panda3d/android/PandaActivity$1.class', opts=OPTS+['DEPENDENCYONLY'], input='PandaActivity.java')
     TargetAdd('org/panda3d/android/PandaActivity$2.class', opts=OPTS+['DEPENDENCYONLY'], input='PandaActivity.java')
     TargetAdd('org/panda3d/android/PythonActivity.class', opts=OPTS, input='PythonActivity.java')
+    TargetAdd('org/panda3d/android/PythonActivity$ActivityResultListener.class', opts=OPTS+['DEPENDENCYONLY'], input='PythonActivity.java')
 
     TargetAdd('classes.dex', input='org/panda3d/android/NativeIStream.class')
     TargetAdd('classes.dex', input='org/panda3d/android/NativeOStream.class')
@@ -4955,6 +4956,7 @@ if GetTarget() == 'android':
     TargetAdd('classes.dex', input='org/panda3d/android/PandaActivity$1.class')
     TargetAdd('classes.dex', input='org/panda3d/android/PandaActivity$2.class')
     TargetAdd('classes.dex', input='org/panda3d/android/PythonActivity.class')
+    TargetAdd('classes.dex', input='org/panda3d/android/PythonActivity$ActivityResultListener.class')
 
     TargetAdd('p3android_composite1.obj', opts=OPTS, input='p3android_composite1.cxx')
     TargetAdd('libp3android.dll', input='p3android_composite1.obj')

+ 11 - 0
panda/src/android/PandaActivity.java

@@ -81,6 +81,16 @@ public class PandaActivity extends NativeActivity {
         return Thread.currentThread().getName();
     }
 
+    /**
+     * Called by android_native_app_glue to spawn the application thread.
+     * Gets passed a function pointer and a data pointer to pass to it.
+     */
+    protected void spawnAppThread(long ptr, long data) {
+        new Thread(() -> {
+            nativeThreadEntry(ptr, data);
+        }).start();
+    }
+
     /**
      * Maps the blob to memory and returns the pointer.
      */
@@ -193,4 +203,5 @@ public class PandaActivity extends NativeActivity {
     }
 
     private static native long nativeMmap(int fd, long off, long len);
+    private static native void nativeThreadEntry(long ptr, long data);
 }

+ 41 - 0
panda/src/android/PythonActivity.java

@@ -15,6 +15,12 @@ package org.panda3d.android;
 
 import org.panda3d.android.PandaActivity;
 
+import android.content.Intent;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
 /**
  * Extends PandaActivity with some things that are useful in a Python
  * application.
@@ -26,4 +32,39 @@ public class PythonActivity extends PandaActivity {
     public PythonActivity() {
         mActivity = this;
     }
+
+    // Helper code to further support plyer.
+    public interface ActivityResultListener {
+        void onActivityResult(int requestCode, int resultCode, Intent data);
+    }
+
+    private List<ActivityResultListener> activityResultListeners = null;
+
+    public void registerActivityResultListener(ActivityResultListener listener) {
+        if (this.activityResultListeners == null) {
+            this.activityResultListeners = Collections.synchronizedList(new ArrayList<ActivityResultListener>());
+        }
+        this.activityResultListeners.add(listener);
+    }
+
+    public void unregisterActivityResultListener(ActivityResultListener listener) {
+        if (this.activityResultListeners == null) {
+            return;
+        }
+        this.activityResultListeners.remove(listener);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+        if (this.activityResultListeners == null) {
+            return;
+        }
+        this.onResume();
+        synchronized (this.activityResultListeners) {
+            Iterator<ActivityResultListener> iterator = this.activityResultListeners.iterator();
+            while (iterator.hasNext()) {
+                iterator.next().onActivityResult(requestCode, resultCode, intent);
+            }
+        }
+    }
 }

+ 9 - 0
panda/src/android/android_native_app_glue.c

@@ -255,10 +255,19 @@ static struct android_app* android_app_create(ANativeActivity* activity,
     android_app->msgread = msgpipe[0];
     android_app->msgwrite = msgpipe[1];
 
+    // Spawn the app thread in Java so it gets a valid class loader.
+    JNIEnv *env = activity->env;
+    jclass activity_class = (*env)->GetObjectClass(env, activity->clazz);
+    jmethodID method = (*env)->GetMethodID(env, activity_class, "spawnAppThread", "(JJ)V");
+    LOGE("got function pointer: %ld, data: %ld, env: %ld", (jlong)(void *)android_app_entry, (jlong)(void *)android_app, (jlong)(void *)env);
+    (*env)->CallVoidMethod(env, activity->clazz, method, (void *)android_app_entry, (void *)android_app);
+
+    /*
     pthread_attr_t attr;
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
     pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
+    */
 
     // Wait for thread to start.
     pthread_mutex_lock(&android_app->mutex);

+ 1 - 1
panda/src/android/android_native_app_glue.h

@@ -168,7 +168,7 @@ struct android_app {
     int msgread;
     int msgwrite;
 
-    pthread_t thread;
+    //pthread_t thread;
 
     struct android_poll_source cmdPollSource;
     struct android_poll_source inputPollSource;

+ 7 - 0
panda/src/android/config_android.cxx

@@ -193,3 +193,10 @@ void android_show_toast(ANativeActivity *activity, const std::string &message, i
   env->CallVoidMethod(activity->clazz, jni_PandaActivity_showToast, jmsg, (jint)duration);
   env->DeleteLocalRef(jmsg);
 }
+
+/**
+ * Returns the JNIEnv pointer corresponding to the current thread.
+ */
+void *SDL_AndroidGetJNIEnv() {
+  return Thread::get_current_thread()->get_jni_env();
+}

+ 5 - 0
panda/src/android/config_android.h

@@ -44,4 +44,9 @@ EXPORT_CLASS Filename android_find_library(ANativeActivity *activity, const std:
 EXPORT_CLASS void android_set_title(ANativeActivity *activity, const std::string &title);
 EXPORT_CLASS void android_show_toast(ANativeActivity *activity, const std::string &message, int duration);
 
+// Used to support pyjnius
+extern "C" {
+  EXPORT_CLASS void *SDL_AndroidGetJNIEnv(void);
+};
+
 #endif

+ 8 - 0
panda/src/android/jni_PandaActivity.cxx

@@ -38,3 +38,11 @@ Java_org_panda3d_android_PandaActivity_nativeMmap(JNIEnv* env, jclass, jint fd,
     return (jlong)0;
   }
 }
+
+/**
+ * Calls the given function pointer, passing the given data pointer.
+ */
+EXPORT_JNI void
+Java_org_panda3d_android_PandaActivity_nativeThreadEntry(JNIEnv* env, jobject self, jlong func, jlong data) {
+  ((void (*)(void *))(void *)func)((void *)data);
+}