Browse Source

android: allow launching pview by opening models (using Intents)

Also add code to show notification toasts.
Also enable model cache by default (pointing to app cache dir)
rdb 7 years ago
parent
commit
ae0f82911b

+ 37 - 7
panda/src/android/PandaActivity.java

@@ -14,6 +14,9 @@
 package org.panda3d.android;
 
 import android.app.NativeActivity;
+import android.content.Intent;
+import android.net.Uri;
+import android.widget.Toast;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import org.panda3d.android.NativeIStream;
@@ -45,14 +48,41 @@ public class PandaActivity extends NativeActivity {
         return Thread.currentThread().getName();
     }
 
+    public String getIntentDataPath() {
+        Intent intent = getIntent();
+        Uri data = intent.getData();
+        if (data == null) {
+            return null;
+        }
+        String path = data.getPath();
+        if (path.startsWith("//")) {
+          path = path.substring(1);
+        }
+        return path;
+    }
+
+    public String getCacheDirString() {
+        return getCacheDir().toString();
+    }
+
+    public void showToast(final String text, final int duration) {
+        final PandaActivity activity = this;
+        runOnUiThread(new Runnable() {
+            public void run() {
+                Toast toast = Toast.makeText(activity, text, duration);
+                toast.show();
+            }
+        });
+    }
+
     static {
-        System.loadLibrary("gnustl_shared");
-        System.loadLibrary("p3dtool");
-        System.loadLibrary("p3dtoolconfig");
-        System.loadLibrary("pandaexpress");
-        System.loadLibrary("panda");
-        System.loadLibrary("p3android");
-        System.loadLibrary("p3framework");
+        //System.loadLibrary("gnustl_shared");
+        //System.loadLibrary("p3dtool");
+        //System.loadLibrary("p3dtoolconfig");
+        //System.loadLibrary("pandaexpress");
+        //System.loadLibrary("panda");
+        //System.loadLibrary("p3android");
+        //System.loadLibrary("p3framework");
         System.loadLibrary("pandaegg");
         System.loadLibrary("pandagles");
     }

+ 41 - 3
panda/src/android/android_main.cxx

@@ -25,7 +25,7 @@
 
 // struct android_app* panda_android_app = NULL;
 
-extern int main(int argc, char **argv);
+extern int main(int argc, const char **argv);
 
 /**
  * This function is called by native_app_glue to initialize the program.  It
@@ -100,6 +100,20 @@ void android_main(struct android_app* app) {
     }
   }
 
+  // Get the cache directory.  Set the model-path to this location.
+  methodID = env->GetMethodID(activity_class, "getCacheDirString", "()Ljava/lang/String;");
+  jstring jcache_dir = (jstring) env->CallObjectMethod(activity->clazz, methodID);
+
+  if (jcache_dir != nullptr) {
+    const char *cache_dir;
+    cache_dir = env->GetStringUTFChars(jcache_dir, nullptr);
+    android_cat.info() << "Path to cache: " << cache_dir << "\n";
+
+    ConfigVariableFilename model_cache_dir("model-cache-dir", Filename());
+    model_cache_dir.set_value(cache_dir);
+    env->ReleaseStringUTFChars(jcache_dir, cache_dir);
+  }
+
   // Get the path to the APK.
   methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;");
   jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID);
@@ -129,9 +143,29 @@ void android_main(struct android_app* app) {
   //TODO: prevent it from adding the directory multiple times.
   get_model_path().append_directory(asset_dir);
 
+  // Also read the intent filename.
+  methodID = env->GetMethodID(activity_class, "getIntentDataPath", "()Ljava/lang/String;");
+  jstring filename = (jstring) env->CallObjectMethod(activity->clazz, methodID);
+  const char *filename_str = nullptr;
+  if (filename != nullptr) {
+    filename_str = env->GetStringUTFChars(filename, nullptr);
+    android_cat.info() << "Got intent filename: " << filename_str << "\n";
+
+    Filename fn(filename_str);
+    if (!fn.exists()) {
+      // Show a toast with the failure message.
+      android_show_toast(activity, string("Unable to access ") + filename_str, 1);
+    }
+  }
+
   // Create bogus argc and argv for calling the main function.
-  char *argv[] = {nullptr};
-  int argc = 0;
+  const char *argv[] = {"pview", nullptr, nullptr};
+  int argc = 1;
+
+  if (filename_str != nullptr) {
+    argv[1] = filename_str;
+    ++argc;
+  }
 
   while (!app->destroyRequested) {
     // Call the main function.  This will not return until the app is done.
@@ -173,6 +207,10 @@ void android_main(struct android_app* app) {
 
   android_cat.info() << "Destroy requested, exiting from android_main\n";
 
+  if (filename_str != nullptr) {
+    env->ReleaseStringUTFChars(filename, filename_str);
+  }
+
   // Detach the thread before exiting.
   activity->vm->DetachCurrentThread();
 }

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

@@ -24,6 +24,7 @@ struct android_app *panda_android_app = NULL;
 jclass    jni_PandaActivity;
 jmethodID jni_PandaActivity_readBitmapSize;
 jmethodID jni_PandaActivity_readBitmap;
+jmethodID jni_PandaActivity_showToast;
 
 jclass   jni_BitmapFactory_Options;
 jfieldID jni_BitmapFactory_Options_outWidth;
@@ -62,6 +63,9 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved) {
   jni_PandaActivity_readBitmap = env->GetStaticMethodID(jni_PandaActivity,
                    "readBitmap", "(JI)Landroid/graphics/Bitmap;");
 
+  jni_PandaActivity_showToast = env->GetMethodID(jni_PandaActivity,
+                   "showToast", "(Ljava/lang/String;I)V");
+
   jni_BitmapFactory_Options = env->FindClass("android/graphics/BitmapFactory$Options");
   jni_BitmapFactory_Options = (jclass) env->NewGlobalRef(jni_BitmapFactory_Options);
 
@@ -83,3 +87,17 @@ void JNI_OnUnload(JavaVM *jvm, void *reserved) {
   env->DeleteGlobalRef(jni_PandaActivity);
   env->DeleteGlobalRef(jni_BitmapFactory_Options);
 }
+
+/**
+ * Shows a toast notification at the bottom of the activity.  The duration
+ * should be 0 for short and 1 for long.
+ */
+void android_show_toast(ANativeActivity *activity, const string &message, int duration) {
+  Thread *thread = Thread::get_current_thread();
+  JNIEnv *env = thread->get_jni_env();
+  nassertv(env != nullptr);
+
+  jstring jmsg = env->NewStringUTF(message.c_str());
+  env->CallVoidMethod(activity->clazz, jni_PandaActivity_showToast, jmsg, (jint)duration);
+  env->DeleteLocalRef(jmsg);
+}

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

@@ -20,6 +20,7 @@
 #include "configVariableBool.h"
 #include "configVariableInt.h"
 
+#include <android/native_activity.h>
 #include <jni.h>
 
 NotifyCategoryDecl(android, EXPORT_CLASS, EXPORT_TEMPL);
@@ -30,9 +31,12 @@ extern EXPORT_CLASS struct android_app* panda_android_app;
 extern jclass    jni_PandaActivity;
 extern jmethodID jni_PandaActivity_readBitmapHeader;
 extern jmethodID jni_PandaActivity_readBitmap;
+extern jmethodID jni_PandaActivity_showToast;
 
 extern jclass   jni_BitmapFactory_Options;
 extern jfieldID jni_BitmapFactory_Options_outWidth;
 extern jfieldID jni_BitmapFactory_Options_outHeight;
 
+EXPORT_CLASS void android_show_toast(ANativeActivity *activity, const string &message, int duration);
+
 #endif

+ 10 - 6
panda/src/android/pview.cxx

@@ -63,12 +63,16 @@ int main(int argc, char **argv) {
     window->enable_keyboard();
     window->setup_trackball();
     framework.get_models().instance_to(window->get_render());
-    // if (argc < 2) { If we have no arguments, get that trusty old triangle
-    // out.  window->load_default_model(framework.get_models()); } else {
-    // window->load_models(framework.get_models(), argc, argv); }
-
-    window->load_model(framework.get_models(), "panda-model.egg");
-    window->load_model(framework.get_models(), "panda-walk4.egg");
+    if (argc < 2) {
+      // If we have no arguments, get that trusty old triangle
+      // out.
+      window->load_default_model(framework.get_models());
+    } else {
+      if (!window->load_models(framework.get_models(), argc, argv)) {
+        framework.close_framework();
+        return 1;
+      }
+    }
 
     window->loop_animations(hierarchy_match_flags);
 

+ 37 - 3
panda/src/android/pview_manifest.xml

@@ -5,11 +5,15 @@
         android:versionCode="1"
         android:versionName="1.0">
 
-    <uses-sdk android:minSdkVersion="9" />
-    <application android:label="Panda Viewer" android:hasCode="true">
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-sdk android:minSdkVersion="21" />
+    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+
+    <application android:label="Panda Viewer" android:hasCode="true" android:debuggable="true">
         <activity android:name="org.panda3d.android.PandaActivity"
                 android:label="Panda Viewer" android:theme="@android:style/Theme.NoTitleBar"
-                android:configChanges="orientation|keyboardHidden">
+                android:configChanges="orientation|keyboardHidden"
+                android:launchMode="singleInstance">
 
             <meta-data android:name="android.app.lib_name"
                        android:value="pview" />
@@ -17,6 +21,36 @@
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="model/x-egg" />
+                <data android:mimeType="model/x-compressed-egg" />
+                <data android:mimeType="model/x-bam" />
+                <data android:mimeType="model/x-compressed-bam" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="*/*" android:scheme="file" />
+                <data android:pathPattern=".*\\.egg" />
+                <data android:pathPattern=".*\\.egg.pz" />
+                <data android:pathPattern=".*\\.egg.gz" />
+                <data android:pathPattern=".*\\.bam" />
+                <data android:pathPattern=".*\\.bam.pz" />
+                <data android:pathPattern=".*\\.bam.gz" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="*/*" scheme="content" host="com.termux.files" />
+                <data android:pathPattern=".*\\.egg" />
+                <data android:pathPattern=".*\\.egg.pz" />
+                <data android:pathPattern=".*\\.egg.gz" />
+                <data android:pathPattern=".*\\.bam" />
+                <data android:pathPattern=".*\\.bam.pz" />
+                <data android:pathPattern=".*\\.bam.gz" />
+            </intent-filter>
         </activity>
     </application>