changeset 2526:d3f09b3f3703

Android: Initial dw_file_browse() implementation, still needs some work. Fix minor issues with the Calendar control.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Mon, 10 May 2021 02:01:28 +0000
parents 9fd26efff9da
children eec926265888
files android/DWindows.kt android/dw.cpp
diffstat 2 files changed, 188 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/android/DWindows.kt	Sun May 09 22:39:13 2021 +0000
+++ b/android/DWindows.kt	Mon May 10 02:01:28 2021 +0000
@@ -1,6 +1,8 @@
 package org.dbsoft.dwindows
 
-import android.R.attr
+import android.R
+import android.app.Activity
+import android.app.Dialog
 import android.app.NotificationChannel
 import android.app.NotificationManager
 import android.content.ClipData
@@ -45,6 +47,7 @@
 import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
 import com.google.android.material.tabs.TabLayoutMediator
 import java.io.File
+import java.io.FileFilter
 import java.io.FileInputStream
 import java.io.FileNotFoundException
 import java.util.*
@@ -236,6 +239,137 @@
     )
 }
 
+class DWFileChooser(private val activity: Activity) {
+    private val list: ListView = ListView(activity)
+    private val dialog: Dialog = Dialog(activity)
+    private var currentPath: File? = null
+
+    // filter on file extension
+    private var extension: String? = null
+    fun setExtension(extension: String?) {
+        this.extension = extension?.toLowerCase(Locale.ROOT)
+    }
+
+    // file selection event handling
+    interface FileSelectedListener {
+        fun fileSelected(file: File?)
+    }
+
+    fun setFileListener(fileListener: FileSelectedListener?): DWFileChooser {
+        this.fileListener = fileListener
+        return this
+    }
+
+    private var fileListener: FileSelectedListener? = null
+    fun showDialog() {
+        dialog.show()
+    }
+
+    /**
+     * Sort, filter and display the files for the given path.
+     */
+    private fun refresh(path: File?) {
+        currentPath = path
+        if (path != null) {
+            if (path.exists()) {
+                val dirs = path.listFiles { file -> file.isDirectory && file.canRead() }
+                val files = path.listFiles { file ->
+                    if (!file.isDirectory) {
+                        if (!file.canRead()) {
+                            false
+                        } else if (extension == null) {
+                            true
+                        } else {
+                            file.name.toLowerCase(Locale.ROOT).endsWith(extension!!)
+                        }
+                    } else {
+                        false
+                    }
+                }
+
+                // convert to an array
+                var i = 0
+                val fileList: Array<String?>
+                var filecount = 0
+                var dircount = 0
+                if(files != null) {
+                    filecount = files.size
+                }
+                if(dirs != null) {
+                    dircount = dirs.size
+                }
+                if (path.parentFile == null) {
+                    fileList = arrayOfNulls(dircount + filecount)
+                } else {
+                    fileList = arrayOfNulls(dircount + filecount + 1)
+                    fileList[i++] = PARENT_DIR
+                }
+                if(dirs != null) {
+                    Arrays.sort(dirs)
+                    for (dir in dirs) {
+                        fileList[i++] = dir.name
+                    }
+                }
+                if(files != null) {
+                    Arrays.sort(files)
+                    for (file in files) {
+                        fileList[i++] = file.name
+                    }
+                }
+
+                // refresh the user interface
+                dialog.setTitle(currentPath!!.path)
+                list.adapter = object : ArrayAdapter<Any?>(
+                    activity,
+                    R.layout.simple_list_item_1, fileList
+                ) {
+                    override fun getView(pos: Int, view: View?, parent: ViewGroup): View {
+                        val thisview = super.getView(pos, view, parent)
+                        (thisview as TextView).isSingleLine = true
+                        return thisview
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Convert a relative filename into an actual File object.
+     */
+    private fun getChosenFile(fileChosen: String): File? {
+        return if (fileChosen == PARENT_DIR) {
+            currentPath!!.parentFile
+        } else {
+            File(currentPath, fileChosen)
+        }
+    }
+
+    companion object {
+        private const val PARENT_DIR = ".."
+    }
+
+    init {
+        list.onItemClickListener =
+            OnItemClickListener { parent, view, which, id ->
+                val fileChosen = list.getItemAtPosition(which) as String
+                val chosenFile: File? = getChosenFile(fileChosen)
+                if (chosenFile != null) {
+                    if (chosenFile.isDirectory) {
+                        refresh(chosenFile)
+                    } else {
+                        if (fileListener != null) {
+                            fileListener!!.fileSelected(chosenFile)
+                        }
+                        dialog.dismiss()
+                    }
+                }
+            }
+        dialog.setContentView(list)
+        dialog.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+        refresh(Environment.getExternalStorageDirectory())
+    }
+}
+
 class DWindows : AppCompatActivity() {
     var firstWindow: Boolean = true
     var windowLayout: LinearLayout? = null
@@ -1557,6 +1691,34 @@
         Log.d(null, text)
     }
 
+    fun fileBrowse(title: String, defpath: String?, ext: String?, flags: Int): String?
+    {
+        var retval: String? = null
+
+        waitOnUiThread {
+            val fc = DWFileChooser(this)
+            fc.setFileListener(object: DWFileChooser.FileSelectedListener {
+                    override fun fileSelected(file: File?) {
+                        // do something with the file
+                        retval = file!!.absolutePath
+                        throw java.lang.RuntimeException()
+                    }
+                })
+            if(ext != null) {
+                fc.setExtension(ext)
+            }
+            fc.showDialog()
+        }
+
+        // loop till a runtime exception is triggered.
+        try {
+            Looper.loop()
+        } catch (e2: RuntimeException) {
+        }
+
+        return retval
+    }
+
     fun messageBox(title: String, body: String, flags: Int): Int
     {
         var retval: Int = 0
--- a/android/dw.cpp	Sun May 09 22:39:13 2021 +0000
+++ b/android/dw.cpp	Mon May 10 02:01:28 2021 +0000
@@ -760,6 +760,29 @@
  */
 char * API dw_file_browse(const char *title, const char *defpath, const char *ext, int flags)
 {
+    JNIEnv *env;
+
+    if((env = (JNIEnv *)pthread_getspecific(_dw_env_key)))
+    {
+        // Use a long parameter
+        jstring jstr = env->NewStringUTF(title);
+        jstring path = NULL;
+        jstring jext = NULL;
+        if(defpath)
+            path = env->NewStringUTF(defpath);
+        if(ext)
+            jext = env->NewStringUTF(defpath);
+        // First get the class that contains the method you need to call
+        //jclass clazz = _dw_find_class(env, DW_CLASS_NAME);
+        jclass clazz = env->FindClass(DW_CLASS_NAME);
+        // Get the method that you want to call
+        jmethodID fileBrowse = env->GetMethodID(clazz, "fileBrowse",
+                                                "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;");
+        // Call the method on the object
+        jstring jresult = (jstring)env->CallObjectMethod(_dw_obj, fileBrowse, jstr, path, jext, flags);
+        if(jresult)
+            return strdup(env->GetStringUTFChars(jresult, 0));
+    }
     return NULL;
 }
 
@@ -3283,7 +3306,7 @@
 
         // Convert to Unix time
         ts.tm_year = year - 1900;
-        ts.tm_mon = month;
+        ts.tm_mon = month - 1;
         ts.tm_mday = day;
         date = mktime(&ts);
 
@@ -3325,7 +3348,7 @@
         if(year)
             *year = ts.tm_year + 1900;
         if(month)
-            *month = ts.tm_mon;
+            *month = ts.tm_mon + 1;
         if(day)
             *day = ts.tm_mday;
     }