Mercurial > dwindows
comparison android/DWindows.kt @ 2658:ad6fc7f1a9af
Android: Asset/Resource refactoring for compatibility with other platforms.
Other platforms are able to access the bundled assets via the filesystem.
Android keeps the assets in the Zipped APK archive, so during initialization
we now extract the non-resource (numeric images) into the app cache dir.
dw_app_dir() now returns the path to the application cache.
dw_user_dir() returns either the HOME directory or the path to the
application root directory (usually the parent of the cache).
author | bsmith@81767d24-ef19-dc11-ae90-00e081727c95 |
---|---|
date | Fri, 24 Sep 2021 13:57:16 +0000 |
parents | 2bdbd5e83654 |
children | 3a14d7fd4b99 |
comparison
equal
deleted
inserted
replaced
2657:9535f533a230 | 2658:ad6fc7f1a9af |
---|---|
51 import androidx.recyclerview.widget.RecyclerView | 51 import androidx.recyclerview.widget.RecyclerView |
52 import androidx.viewpager2.widget.ViewPager2 | 52 import androidx.viewpager2.widget.ViewPager2 |
53 import com.google.android.material.tabs.TabLayout | 53 import com.google.android.material.tabs.TabLayout |
54 import com.google.android.material.tabs.TabLayout.OnTabSelectedListener | 54 import com.google.android.material.tabs.TabLayout.OnTabSelectedListener |
55 import com.google.android.material.tabs.TabLayoutMediator | 55 import com.google.android.material.tabs.TabLayoutMediator |
56 import java.io.BufferedInputStream | |
56 import java.io.File | 57 import java.io.File |
58 import java.io.FileOutputStream | |
57 import java.io.IOException | 59 import java.io.IOException |
58 import java.util.* | 60 import java.util.* |
59 import java.util.concurrent.locks.ReentrantLock | 61 import java.util.concurrent.locks.ReentrantLock |
62 import java.util.zip.ZipEntry | |
63 import java.util.zip.ZipFile | |
60 | 64 |
61 object DWEvent { | 65 object DWEvent { |
62 const val TIMER = 0 | 66 const val TIMER = 0 |
63 const val CONFIGURE = 1 | 67 const val CONFIGURE = 1 |
64 const val KEY_PRESS = 2 | 68 const val KEY_PRESS = 2 |
79 const val COLUMN_CLICK = 17 | 83 const val COLUMN_CLICK = 17 |
80 const val HTML_RESULT = 18 | 84 const val HTML_RESULT = 18 |
81 const val HTML_CHANGED = 19 | 85 const val HTML_CHANGED = 19 |
82 } | 86 } |
83 | 87 |
88 val DWImageExts = arrayOf("", ".png", ".webp", ".jpg", ".jpeg", ".gif") | |
89 | |
84 class DWTabViewPagerAdapter : RecyclerView.Adapter<DWTabViewPagerAdapter.DWEventViewHolder>() { | 90 class DWTabViewPagerAdapter : RecyclerView.Adapter<DWTabViewPagerAdapter.DWEventViewHolder>() { |
85 val viewList = mutableListOf<LinearLayout>() | 91 val viewList = mutableListOf<LinearLayout>() |
86 val pageList = mutableListOf<Long>() | 92 val pageList = mutableListOf<Long>() |
87 var currentPageID = 0L | 93 var currentPageID = 0L |
88 | 94 |
794 threadCond.await() | 800 threadCond.await() |
795 threadLock.unlock() | 801 threadLock.unlock() |
796 } | 802 } |
797 } | 803 } |
798 | 804 |
805 // Returns true if the filename is a resource ID (non-zero number) | |
806 // with a image file extension in our DWImageExts list | |
807 private fun isDWResource(filename: String): Boolean { | |
808 val length = filename.length | |
809 | |
810 for (ext in DWImageExts) { | |
811 if (ext.isNotEmpty() && filename.endsWith(ext)) { | |
812 val filebody: String = filename.substring(7, length - ext.length) | |
813 if (filebody.toInt() > 0) { | |
814 return true | |
815 } | |
816 } | |
817 } | |
818 return false | |
819 } | |
820 | |
821 /** | |
822 * extracts assets/ in the APK to the application cache directory | |
823 */ | |
824 private fun extractAssets() { | |
825 var zipFile: ZipFile? = null | |
826 val targetDir = cacheDir | |
827 | |
828 try { | |
829 zipFile = ZipFile(this.applicationInfo.sourceDir) | |
830 val e: Enumeration<out ZipEntry?> = zipFile.entries() | |
831 while (e.hasMoreElements()) { | |
832 val entry: ZipEntry? = e.nextElement() | |
833 if (entry == null || entry.isDirectory || !entry.name.startsWith("assets/") || | |
834 isDWResource(entry.name)) { | |
835 continue | |
836 } | |
837 val targetFile = File(targetDir, entry.name.substring("assets/".length)) | |
838 targetFile.parentFile!!.mkdirs() | |
839 val tempBuffer = ByteArray(entry.size.toInt()) | |
840 var ais: BufferedInputStream? = null | |
841 var aos: FileOutputStream? = null | |
842 try { | |
843 ais = BufferedInputStream(zipFile.getInputStream(entry)) | |
844 aos = FileOutputStream(targetFile) | |
845 ais.read(tempBuffer) | |
846 aos.write(tempBuffer) | |
847 } catch (e: IOException) { | |
848 } finally { | |
849 ais?.close() | |
850 aos?.close() | |
851 } | |
852 } | |
853 } catch (e: IOException) { | |
854 } finally { | |
855 zipFile?.close() | |
856 } | |
857 } | |
858 | |
799 // We only want to call this once when the app starts up | 859 // We only want to call this once when the app starts up |
800 // By default Android will call onCreate for rotation and other | 860 // By default Android will call onCreate for rotation and other |
801 // changes. This is incompatible with Dynamic Windows... | 861 // changes. This is incompatible with Dynamic Windows... |
802 // Make sure the following is in your AndroidManifest.xml | 862 // Make sure the following is in your AndroidManifest.xml |
803 // android:configChanges="orientation|screenSize|screenLayout|keyboardHidden" | 863 // android:configChanges="orientation|screenSize|screenLayout|keyboardHidden" |
810 // Get the Android app path | 870 // Get the Android app path |
811 val m = packageManager | 871 val m = packageManager |
812 var s = packageName | 872 var s = packageName |
813 val p = m.getPackageInfo(s!!, 0) | 873 val p = m.getPackageInfo(s!!, 0) |
814 s = p.applicationInfo.dataDir | 874 s = p.applicationInfo.dataDir |
875 val c = cacheDir.path | |
876 | |
877 // Extract any non-resource assets to the cache directory | |
878 // So that our C code can access them as files, like on | |
879 // other Dynamic Windows platforms | |
880 extractAssets() | |
815 | 881 |
816 // Initialize the Dynamic Windows code... | 882 // Initialize the Dynamic Windows code... |
817 // This will start a new thread that calls the app's dwmain() | 883 // This will start a new thread that calls the app's dwmain() |
818 dwindowsInit(s, this.getPackageName()) | 884 dwindowsInit(s, c, this.getPackageName()) |
819 } | 885 } |
820 | 886 |
821 override fun onConfigurationChanged(newConfig: Configuration) { | 887 override fun onConfigurationChanged(newConfig: Configuration) { |
822 super.onConfigurationChanged(newConfig) | 888 super.onConfigurationChanged(newConfig) |
823 | 889 |
1464 fun bitmapButtonNew(text: String, resid: Int): ImageButton? { | 1530 fun bitmapButtonNew(text: String, resid: Int): ImageButton? { |
1465 var button: ImageButton? = null | 1531 var button: ImageButton? = null |
1466 waitOnUiThread { | 1532 waitOnUiThread { |
1467 button = ImageButton(this) | 1533 button = ImageButton(this) |
1468 val dataArrayMap = SimpleArrayMap<String, Long>() | 1534 val dataArrayMap = SimpleArrayMap<String, Long>() |
1469 val exts = arrayOf("", ".png", ".webp", ".jpg", ".jpeg", ".gif") | |
1470 var filename: String? = null | 1535 var filename: String? = null |
1471 | 1536 |
1472 button!!.tag = dataArrayMap | 1537 button!!.tag = dataArrayMap |
1473 button!!.id = resid | 1538 button!!.id = resid |
1474 button!!.setImageResource(resid) | 1539 button!!.setImageResource(resid) |
1480 if(resid > 0 && resid < 65536) { | 1545 if(resid > 0 && resid < 65536) { |
1481 filename = resid.toString() | 1546 filename = resid.toString() |
1482 } | 1547 } |
1483 | 1548 |
1484 if(filename != null) { | 1549 if(filename != null) { |
1485 for (ext in exts) { | 1550 for (ext in DWImageExts) { |
1486 // Try to load the image, and protect against exceptions | 1551 // Try to load the image, and protect against exceptions |
1487 try { | 1552 try { |
1488 val f = this.assets.open(filename + ext) | 1553 val f = this.assets.open(filename + ext) |
1489 val b = BitmapFactory.decodeStream(f) | 1554 val b = BitmapFactory.decodeStream(f) |
1490 | 1555 |
1503 fun bitmapButtonNewFromFile(text: String, cid: Int, filename: String): ImageButton? { | 1568 fun bitmapButtonNewFromFile(text: String, cid: Int, filename: String): ImageButton? { |
1504 var button: ImageButton? = null | 1569 var button: ImageButton? = null |
1505 waitOnUiThread { | 1570 waitOnUiThread { |
1506 button = ImageButton(this) | 1571 button = ImageButton(this) |
1507 val dataArrayMap = SimpleArrayMap<String, Long>() | 1572 val dataArrayMap = SimpleArrayMap<String, Long>() |
1508 val exts = arrayOf("", ".png", ".webp", ".jpg", ".jpeg", ".gif") | |
1509 | 1573 |
1510 button!!.tag = dataArrayMap | 1574 button!!.tag = dataArrayMap |
1511 button!!.id = cid | 1575 button!!.id = cid |
1512 button!!.setOnClickListener { | 1576 button!!.setOnClickListener { |
1513 lastClickView = button!! | 1577 lastClickView = button!! |
1514 eventHandlerSimple(button!!, DWEvent.CLICKED) | 1578 eventHandlerSimple(button!!, DWEvent.CLICKED) |
1515 } | 1579 } |
1516 | 1580 |
1517 for (ext in exts) { | 1581 for (ext in DWImageExts) { |
1518 // Try to load the image, and protect against exceptions | 1582 // Try to load the image, and protect against exceptions |
1519 try { | 1583 try { |
1520 val f = this.assets.open(filename + ext) | 1584 val f = this.assets.open(filename + ext) |
1521 val b = BitmapFactory.decodeStream(f) | 1585 val b = BitmapFactory.decodeStream(f) |
1522 | 1586 |
2978 | 3042 |
2979 imageview.setImageResource(resID) | 3043 imageview.setImageResource(resID) |
2980 } | 3044 } |
2981 } | 3045 } |
2982 if(filename != null) { | 3046 if(filename != null) { |
2983 val exts = arrayOf("", ".png", ".webp", ".jpg", ".jpeg", ".gif") | 3047 for (ext in DWImageExts) { |
2984 | |
2985 for (ext in exts) { | |
2986 // Try to load the image, and protect against exceptions | 3048 // Try to load the image, and protect against exceptions |
2987 try { | 3049 try { |
2988 val f = this.assets.open(filename + ext) | 3050 val f = this.assets.open(filename + ext) |
2989 val b = BitmapFactory.decodeStream(f) | 3051 val b = BitmapFactory.decodeStream(f) |
2990 | 3052 |
3060 filename = file | 3122 filename = file |
3061 } | 3123 } |
3062 // Handle filename or DW resource IDs | 3124 // Handle filename or DW resource IDs |
3063 // these will be located in the assets folder | 3125 // these will be located in the assets folder |
3064 if(filename != null) { | 3126 if(filename != null) { |
3065 val exts = arrayOf("", ".png", ".webp", ".jpg", ".jpeg", ".gif") | 3127 for (ext in DWImageExts) { |
3066 | |
3067 for (ext in exts) { | |
3068 // Try to load the image, and protect against exceptions | 3128 // Try to load the image, and protect against exceptions |
3069 try { | 3129 try { |
3070 val f = this.assets.open(filename + ext) | 3130 val f = this.assets.open(filename + ext) |
3071 icon = Drawable.createFromStream(f, null) | 3131 icon = Drawable.createFromStream(f, null) |
3072 } catch (e: IOException) { | 3132 } catch (e: IOException) { |
3097 pixmap = BitmapFactory.decodeByteArray(data, 0, length) | 3157 pixmap = BitmapFactory.decodeByteArray(data, 0, length) |
3098 } else { | 3158 } else { |
3099 filename = file | 3159 filename = file |
3100 } | 3160 } |
3101 if(filename != null) { | 3161 if(filename != null) { |
3102 val exts = arrayOf("", ".png", ".webp", ".jpg", ".jpeg", ".gif") | 3162 for (ext in DWImageExts) { |
3103 | |
3104 for (ext in exts) { | |
3105 // Try to load the image, and protect against exceptions | 3163 // Try to load the image, and protect against exceptions |
3106 try { | 3164 try { |
3107 val f = this.assets.open(filename + ext) | 3165 val f = this.assets.open(filename + ext) |
3108 pixmap = BitmapFactory.decodeStream(f) | 3166 pixmap = BitmapFactory.decodeStream(f) |
3109 } catch (e: IOException) { | 3167 } catch (e: IOException) { |
3982 | 4040 |
3983 /* | 4041 /* |
3984 * Native methods that are implemented by the 'dwindows' native library, | 4042 * Native methods that are implemented by the 'dwindows' native library, |
3985 * which is packaged with this application. | 4043 * which is packaged with this application. |
3986 */ | 4044 */ |
3987 external fun dwindowsInit(dataDir: String, appid: String) | 4045 external fun dwindowsInit(dataDir: String, cacheDir: String, appid: String) |
3988 external fun eventHandler( | 4046 external fun eventHandler( |
3989 obj1: View?, | 4047 obj1: View?, |
3990 obj2: View?, | 4048 obj2: View?, |
3991 message: Int, | 4049 message: Int, |
3992 str1: String?, | 4050 str1: String?, |