# HG changeset patch # User bsmith@81767d24-ef19-dc11-ae90-00e081727c95 # Date 1657724258 0 # Node ID 20d39af27aa4b1aaa757a4ff3c83a4e3ba2e7d47 # Parent 025b4e3e7e75b3cb9cd26fb35e5d68dc6417905d 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. diff -r 025b4e3e7e75 -r 20d39af27aa4 android/DWindows.kt --- 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 } diff -r 025b4e3e7e75 -r 20d39af27aa4 android/dw.cpp --- 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); diff -r 025b4e3e7e75 -r 20d39af27aa4 dw.h --- 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);