changeset 2790:20d39af27aa4

Android: Add a new function for Android dw_file_open() which will open the URI that dw_file_browse() now saves at the end of the returned path. This is a hacky method of letting things function on Android... since Android seems to not let you open files with a pure path. Even if the file exists it will fail to open, you need to use the ugly Android URIs. Not sure if I will keep this solution or not, but committing it so Dynamic Windows Interface Builder will function on Android in the meantime. dw_file_open() just calls the system open() on non-Android platforms.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Wed, 13 Jul 2022 14:57:38 +0000
parents 025b4e3e7e75
children b62859fbd9a3
files android/DWindows.kt android/dw.cpp dw.h
diffstat 3 files changed, 87 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/android/DWindows.kt	Wed Jul 13 08:26:49 2022 +0000
+++ b/android/DWindows.kt	Wed Jul 13 14:57:38 2022 +0000
@@ -28,6 +28,7 @@
 import android.print.pdf.PrintedPdfDocument
 import android.provider.DocumentsContract
 import android.provider.MediaStore
+import android.system.OsConstants
 import android.text.InputFilter
 import android.text.InputFilter.LengthFilter
 import android.text.InputType
@@ -60,10 +61,7 @@
 import com.google.android.material.tabs.TabLayout
 import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
 import com.google.android.material.tabs.TabLayoutMediator
-import java.io.BufferedInputStream
-import java.io.File
-import java.io.FileOutputStream
-import java.io.IOException
+import java.io.*
 import java.util.*
 import java.util.concurrent.locks.ReentrantLock
 import java.util.zip.ZipEntry
@@ -2857,8 +2855,9 @@
         }
     }
 
-    fun windowSetStyle(window: View, style: Int, mask: Int)
-    {
+    fun windowSetStyle(window: Any, style: Int, mask: Int)
+    {
+        // TODO: Need to handle menu items and others
         waitOnUiThread {
             if (window is TextView && window !is EditText) {
                 val ourmask = (Gravity.HORIZONTAL_GRAVITY_MASK or Gravity.VERTICAL_GRAVITY_MASK) and mask
@@ -6100,10 +6099,34 @@
         return null
     }
 
+    fun fileOpen(filename: String, mode: Int): Int
+    {
+        var retval: Int = -1
+        var uri = Uri.parse(filename)
+        var smode: String = "r"
+        var fd: ParcelFileDescriptor? = null
+
+        if((mode and OsConstants.O_WRONLY) == OsConstants.O_WRONLY) {
+            smode = "w"
+        } else if((mode and OsConstants.O_RDWR) == OsConstants.O_RDWR) {
+            smode = "rw"
+        }
+        try {
+            fd = contentResolver.openFileDescriptor(uri, smode)
+        } catch (e: FileNotFoundException) {
+            fd = null
+        }
+        if (fd != null) {
+            retval = fd.fd
+        }
+        return retval
+    }
+
     // Defpath does not seem to be supported on Android using the ACTION_GET_CONTENT Intent
     fun fileBrowseNew(title: String, defpath: String?, ext: String?, flags: Int): String?
     {
         var retval: String? = null
+        var uristr: String? = null
         var permission = Manifest.permission.WRITE_EXTERNAL_STORAGE
         var permissions: Int = -1
 
@@ -6137,6 +6160,9 @@
                 fileCond.await()
                 fileLock.unlock()
 
+                // Save the URI string for later use
+                uristr = fileURI.toString()
+
                 if (DocumentsContract.isDocumentUri(this, fileURI)) {
                     // ExternalStorageProvider
                     if (fileURI?.authority == "com.android.externalstorage.documents") {
@@ -6194,6 +6220,9 @@
                 retval = fileBrowse(title, defpath, ext, flags)
             }
         }
+        if(retval != null && uristr != null) {
+            return retval + "\n\n" + uristr
+        }
         return retval
     }
 
--- a/android/dw.cpp	Wed Jul 13 08:26:49 2022 +0000
+++ b/android/dw.cpp	Wed Jul 13 14:57:38 2022 +0000
@@ -1278,7 +1278,52 @@
         // Call the method on the object
         jstring jresult = (jstring)_dw_jni_check_result(env, env->CallObjectMethod(_dw_obj, fileBrowse, jstr, path, jext, flags), _DW_REFERENCE_NONE);
         if(jresult)
-            retval = strdup(env->GetStringUTFChars(jresult, nullptr));
+        {
+            const char *str = env->GetStringUTFChars(jresult, nullptr);
+            if(str)
+            {
+                size_t len = strlen(str);
+
+                // Allocate a string with an extra two bytes... so we can
+                // check for the trailing \n in dw_file_open() without a
+                // memory violation.
+                if((retval = (char *)calloc(1, len + 2)))
+                {
+                    char *tmp;
+
+                    strncpy(retval, str, len);
+                    // If we have a URI encoded, find the double \n and replace the
+                    // first \n with NULL so the string still looks like a normal path.
+                    tmp = strstr(retval, "\n\n");
+                    if (tmp)
+                        *tmp = 0;
+                }
+            }
+        }
+    }
+    return retval;
+}
+
+int API dw_file_open(const char *path, int mode)
+{
+    JNIEnv *env;
+    int retval = -1;
+
+    if((env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
+    {
+        // dw_file_browse saves a second string with the URI after the path
+        // So find the end of the string and check for a trailing \n
+        // The URI will be after that if found.
+        const char *uri = strchr(path, 0);
+        jstring jstr = env->NewStringUTF((uri && *(uri+1) == '\n') ? (uri+2) : path);
+        // First get the class that contains the method you need to call
+        jclass clazz = _dw_find_class(env, DW_CLASS_NAME);
+        // Get the method that you want to call
+        jmethodID fileOpen = env->GetMethodID(clazz, "fileOpen",
+                                              "(Ljava/lang/String;I)I");
+        // Call the method on the object
+        retval = (int)env->CallIntMethod(_dw_obj, fileOpen, jstr, (jint)mode);
+        _dw_jni_check_exception(env);
     }
     return retval;
 }
@@ -5736,7 +5781,7 @@
         jclass clazz = _dw_find_class(env, DW_CLASS_NAME);
         // Get the method that you want to call
         jmethodID windowSetStyle = env->GetMethodID(clazz, "windowSetStyle",
-                                                    "(Landroid/view/View;II)V");
+                                                    "(Ljava/lang/Object;II)V");
         // Call the method on the object
         env->CallVoidMethod(_dw_obj, windowSetStyle, handle, (jint)style, (jint)mask);
         _dw_jni_check_exception(env);
--- a/dw.h	Wed Jul 13 08:26:49 2022 +0000
+++ b/dw.h	Wed Jul 13 14:57:38 2022 +0000
@@ -2163,6 +2163,11 @@
 void API dw_environment_query(DWEnv *env);
 int API dw_exec(const char *program, int type, char **params);
 int API dw_browse(const char *url);
+#if defined(__ANDROID__)
+int API dw_file_open(const char *path, int mode);
+#else
+#define dw_file_open(a, b) open(a, b)
+#endif
 char * API dw_file_browse(const char *title, const char *defpath, const char *ext, int flags);
 char * API dw_user_dir(void);
 char * API dw_app_dir(void);