# HG changeset patch # User bsmith@81767d24-ef19-dc11-ae90-00e081727c95 # Date 1619952381 0 # Node ID b3e28eed0e509dec3fbf403a2845c1f09620fdcf # Parent bca7e0ab0ccc9feab7156540da1ebb27ada758a4 Android: Fix the basics of notebook control... return actual page IDs. Also need to ensure the box packed in notebookPagePack is MATCH_PARENT. Also prevent multiple calls to dwindowsInit() from spawning multiple instances of dwmain(). diff -r bca7e0ab0ccc -r b3e28eed0e50 android/DWindows.kt --- a/android/DWindows.kt Sun May 02 01:05:20 2021 +0000 +++ b/android/DWindows.kt Sun May 02 10:46:21 2021 +0000 @@ -32,6 +32,8 @@ class DWTabViewPagerAdapter : RecyclerView.Adapter() { public val viewList = mutableListOf() + public val pageList = mutableListOf() + public var currentPageID = 0L override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = EventViewHolder(viewList.get(0)) @@ -362,27 +364,31 @@ notebook.tag = dataArrayMap notebook.id = cid + tabs.id = View.generateViewId() + pager.id = View.generateViewId() pager.adapter = DWTabViewPagerAdapter() TabLayoutMediator(tabs, pager) { tab, position -> - tab.text = "OBJECT ${(position + 1)}" + //tab.text = "OBJECT ${(position + 1)}" }.attach() var params: RelativeLayout.LayoutParams = RelativeLayout.LayoutParams(w, h) - tabs.layoutParams = params - notebook.addView(tabs) - if (top != 0) { - notebook.addView(pager, 0) + if(top != 0) { + params.addRule(RelativeLayout.ABOVE, pager.id) } else { - notebook.addView(pager) + params.addRule(RelativeLayout.BELOW, pager.id) } + tabs.tabGravity = TabLayout.GRAVITY_FILL + tabs.tabMode = TabLayout.MODE_FIXED + notebook.addView(tabs, params) + notebook.addView(pager, RelativeLayout.LayoutParams(w, w)) return notebook } - fun notebookPageNew(notebook: RelativeLayout, flags: Long, front: Int): Any? + fun notebookPageNew(notebook: RelativeLayout, flags: Long, front: Int): Long { var pager: ViewPager2? = null var tabs: TabLayout? = null - var tab: Any? = null + var pageID = 0L if(notebook.getChildAt(0) is ViewPager2 && notebook.getChildAt(1) is TabLayout) { pager = notebook.getChildAt(0) as ViewPager2 @@ -394,21 +400,29 @@ if(pager != null && tabs != null) { var adapter: DWTabViewPagerAdapter = pager.adapter as DWTabViewPagerAdapter + var tab = tabs.newTab() - tab = tabs.newTab() + // Increment our page ID... making sure no duplicates exist + do { + adapter.currentPageID += 1 + } + while(adapter.currentPageID == 0L || adapter.pageList.contains(adapter.currentPageID)) + pageID = adapter.currentPageID // Temporarily add a black tab with an empty layout/box if(front != 0) { adapter.viewList.add(0, LinearLayout(this)) + adapter.pageList.add(0, pageID) tabs.addTab(tab, 0) } else { adapter.viewList.add(LinearLayout(this)) + adapter.pageList.add(pageID) tabs.addTab(tab) } } - return tab + return pageID } - fun notebookPageDestroy(notebook: RelativeLayout, tab: TabLayout.Tab) + fun notebookPageDestroy(notebook: RelativeLayout, pageID: Long) { var pager: ViewPager2? = null var tabs: TabLayout? = null @@ -423,13 +437,18 @@ if(pager != null && tabs != null) { var adapter: DWTabViewPagerAdapter = pager.adapter as DWTabViewPagerAdapter + val index = adapter.pageList.indexOf(pageID) + val tab = tabs.getTabAt(index) - adapter.viewList.removeAt(tab.position) - tabs.removeTab(tab) + if (tab != null) { + adapter.viewList.removeAt(index) + adapter.pageList.removeAt(index) + tabs.removeTab(tab) + } } } - fun notebookPageSetText(notebook: RelativeLayout, tab: TabLayout.Tab, text: String) + fun notebookPageSetText(notebook: RelativeLayout, pageID: Long, text: String) { var pager: ViewPager2? = null var tabs: TabLayout? = null @@ -443,11 +462,17 @@ } if(pager != null && tabs != null) { - tab.text = text + val adapter: DWTabViewPagerAdapter = pager.adapter as DWTabViewPagerAdapter + val index = adapter.pageList.indexOf(pageID) + val tab = tabs.getTabAt(index) + + if (tab != null) { + tab.text = text + } } } - fun notebookPack(notebook: RelativeLayout, tab: TabLayout.Tab, box: LinearLayout) + fun notebookPagePack(notebook: RelativeLayout, pageID: Long, box: LinearLayout) { var pager: ViewPager2? = null var tabs: TabLayout? = null @@ -462,12 +487,17 @@ if(pager != null && tabs != null) { var adapter: DWTabViewPagerAdapter = pager.adapter as DWTabViewPagerAdapter + val index = adapter.pageList.indexOf(pageID) - adapter.viewList[tab.position] = box + // Make sure the box is MATCH_PARENT + box.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT); + + adapter.viewList[index] = box } } - fun notebookPageGet(notebook: RelativeLayout): TabLayout.Tab? + fun notebookPageGet(notebook: RelativeLayout): Long { var pager: ViewPager2? = null var tabs: TabLayout? = null @@ -481,12 +511,13 @@ } if(pager != null && tabs != null) { - return tabs.getTabAt(tabs.selectedTabPosition) + var adapter: DWTabViewPagerAdapter = pager.adapter as DWTabViewPagerAdapter + return adapter.pageList.get(tabs.selectedTabPosition) } - return null + return 0L } - fun notebookPageSet(notebook: RelativeLayout, tab: TabLayout.Tab) + fun notebookPageSet(notebook: RelativeLayout, pageID: Long) { var pager: ViewPager2? = null var tabs: TabLayout? = null @@ -500,6 +531,10 @@ } if(pager != null && tabs != null) { + val adapter: DWTabViewPagerAdapter = pager.adapter as DWTabViewPagerAdapter + val index = adapter.pageList.indexOf(pageID) + val tab = tabs.getTabAt(index) + tabs.selectTab(tab) } } @@ -632,14 +667,20 @@ fun dwindowsExit(exitcode: Int) { - this.finishActivity(exitcode) + this.finishAffinity() + System.exit(exitcode) + } + + fun dwindowsShutdown() + { + this.finishAffinity() } /* * Native methods that are implemented by the 'dwindows' native library, * which is packaged with this application. */ - external fun dwindowsInit(dataDir: String): String + external fun dwindowsInit(dataDir: String) external fun eventHandler(obj1: View, obj2: View?, message: Int, str1: String?, str2: String?, int1: Int, int2: Int, int3: Int, int4: Int): Int external fun eventHandlerSimple(obj1: View, message: Int) external fun eventHandlerTimer(sigfunc: Long, data: Long): Int diff -r bca7e0ab0ccc -r b3e28eed0e50 android/dw.cpp --- a/android/dw.cpp Sun May 02 01:05:20 2021 +0000 +++ b/android/dw.cpp Sun May 02 10:46:21 2021 +0000 @@ -84,27 +84,33 @@ * Parameters: * path: The path to the Android app. */ -JNIEXPORT jstring JNICALL +JNIEXPORT void JNICALL Java_org_dbsoft_dwindows_DWindows_dwindowsInit(JNIEnv* env, jobject obj, jstring path) { - char *arg = strdup(env->GetStringUTFChars((jstring) path, NULL)); - - /* Save our class object pointer for later */ - _dw_obj = env->NewGlobalRef(obj); - - /* Save the JNIEnv for the main thread */ - pthread_key_create(&_dw_env_key, NULL); - pthread_setspecific(_dw_env_key, env); - - /* Create the dwmain event */ - _dw_main_event = dw_event_new(); - - /* Launch the new thread to execute dwmain() */ - dw_thread_new((void *)_dw_main_launch, arg, 0); - - /* Wait until dwmain() calls dw_main() then return */ - dw_event_wait(_dw_main_event, DW_TIMEOUT_INFINITE); - return env->NewStringUTF("Hello from JNI!"); + static int runcount = 0; + + /* Safety check to prevent multiple initializations */ + if(runcount == 1) + { + char *arg = strdup(env->GetStringUTFChars((jstring) path, NULL)); + + /* Save our class object pointer for later */ + _dw_obj = env->NewGlobalRef(obj); + + /* Save the JNIEnv for the main thread */ + pthread_key_create(&_dw_env_key, NULL); + pthread_setspecific(_dw_env_key, env); + + /* Create the dwmain event */ + _dw_main_event = dw_event_new(); + + /* Launch the new thread to execute dwmain() */ + dw_thread_new((void *) _dw_main_launch, arg, 0); + + /* Wait until dwmain() calls dw_main() then return */ + dw_event_wait(_dw_main_event, DW_TIMEOUT_INFINITE); + } + runcount++; } typedef struct _sighandler @@ -484,6 +490,7 @@ // Call the method on the object env->CallVoidMethod(_dw_obj, dwindowsExit, exitcode); } + // We shouldn't get here, but in case JNI can't call dwindowsExit... exit(exitcode); } @@ -2969,9 +2976,9 @@ jclass clazz = _dw_find_class(env, DW_CLASS_NAME); // Get the method that you want to call jmethodID notebookPageNew = env->GetMethodID(clazz, "notebookPageNew", - "(Landroid/widget/RelativeLayout;JI)Ljava/lang/Object;"); + "(Landroid/widget/RelativeLayout;JI)J"); // Call the method on the object - result = DW_POINTER_TO_INT(env->NewWeakGlobalRef(env->CallObjectMethod(_dw_obj, notebookPageNew, handle, (jlong)flags, front))); + result = (unsigned long)env->CallLongMethod(_dw_obj, notebookPageNew, handle, (jlong)flags, front); } return result; } @@ -2988,18 +2995,13 @@ if(handle && pageid && (env = (JNIEnv *)pthread_getspecific(_dw_env_key))) { - jobject tab = (jobject)DW_INT_TO_POINTER(pageid); - // 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 notebookPageDestroy = env->GetMethodID(clazz, "notebookPageDestroy", - "(Landroid/widget/RelativeLayout;Lcom/google/android/material/tabs/TabLayout/Tab;)V"); + "(Landroid/widget/RelativeLayout;J)V"); // Call the method on the object - env->CallVoidMethod(_dw_obj, notebookPageDestroy, handle, tab); - - // Release the global reference - env->DeleteWeakGlobalRef(tab); + env->CallVoidMethod(_dw_obj, notebookPageDestroy, handle, (jlong)pageid); } } @@ -3020,9 +3022,9 @@ jclass clazz = _dw_find_class(env, DW_CLASS_NAME); // Get the method that you want to call jmethodID notebookPageGet = env->GetMethodID(clazz, "notebookPageGet", - "(Landroid/widget/RelativeLayout;)Lcom/google/android/material/tabs/TabLayout/Tab;"); + "(Landroid/widget/RelativeLayout;)J"); // Call the method on the object - result = DW_POINTER_TO_INT(env->CallObjectMethod(_dw_obj, notebookPageGet, handle)); + result = (unsigned long)env->CallLongMethod(_dw_obj, notebookPageGet, handle); } return result; } @@ -3038,15 +3040,13 @@ JNIEnv *env; if(handle && pageid && (env = (JNIEnv *)pthread_getspecific(_dw_env_key))) { - jobject tab = (jobject) DW_INT_TO_POINTER(pageid); - // 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 notebookPageSet = env->GetMethodID(clazz, "notebookPageSet", - "(Landroid/widget/RelativeLayout;Lcom/google/android/material/tabs/TabLayout/Tab;)V"); + "(Landroid/widget/RelativeLayout;J)V"); // Call the method on the object - env->CallVoidMethod(_dw_obj, notebookPageSet, handle, tab); + env->CallVoidMethod(_dw_obj, notebookPageSet, handle, (jlong)pageid); } } @@ -3063,17 +3063,15 @@ if((env = (JNIEnv *)pthread_getspecific(_dw_env_key))) { - jobject tab = (jobject)DW_INT_TO_POINTER(pageid); - // Construct a String jstring jstr = env->NewStringUTF(text); // 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 notebookPageSetText = env->GetMethodID(clazz, "notebookPageSetText", - "(Landroid/widget/RelativeLayout;Lcom/google/android/material/tabs/TabLayout/Tab;Ljava/lang/String;)V"); + "(Landroid/widget/RelativeLayout;JLjava/lang/String;)V"); // Call the method on the object - env->CallVoidMethod(_dw_obj, notebookPageSetText, handle, tab, jstr); + env->CallVoidMethod(_dw_obj, notebookPageSetText, handle, (jlong)pageid, jstr); } } @@ -3101,15 +3099,13 @@ if(handle && pageid && (env = (JNIEnv *)pthread_getspecific(_dw_env_key))) { - jobject tab = (jobject)DW_INT_TO_POINTER(pageid); - // 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 notebookPack = env->GetMethodID(clazz, "notebookPack", - "(Landroid/widget/RelativeLayout;Lcom/google/android/material/tabs/TabLayout/Tab;Landroid/widget/LinearLayout;)V"); + jmethodID notebookPagePack = env->GetMethodID(clazz, "notebookPagePack", + "(Landroid/widget/RelativeLayout;JLandroid/widget/LinearLayout;)V"); // Call the method on the object - env->CallVoidMethod(_dw_obj, notebookPack, handle, tab, page); + env->CallVoidMethod(_dw_obj, notebookPagePack, handle, (jlong)pageid, page); } } @@ -3787,7 +3783,7 @@ if(timerid && (env = (JNIEnv *)pthread_getspecific(_dw_env_key))) { - // Use a long paramater + // Use a long parameter jobject timer = (jobject)timerid; // First get the class that contains the method you need to call jclass clazz = _dw_find_class(env, DW_CLASS_NAME); @@ -4802,6 +4798,18 @@ */ void API dw_shutdown(void) { + JNIEnv *env; + + if((env = (JNIEnv *)pthread_getspecific(_dw_env_key))) + { + // 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 dwindowsShutdown = env->GetMethodID(clazz, "dwindowsShutdown", + "()V"); + // Call the method on the object + env->CallVoidMethod(_dw_obj, dwindowsShutdown); + } } /* @@ -4949,24 +4957,15 @@ { switch(feature) { -#if 0 case DW_FEATURE_HTML: /* Supports the HTML Widget */ case DW_FEATURE_HTML_RESULT: /* Supports the DW_SIGNAL_HTML_RESULT callback */ - case DW_FEATURE_WINDOW_BORDER: /* Supports custom window border sizes */ - case DW_FEATURE_WINDOW_TRANSPARENCY: /* Supports window frame transparency */ case DW_FEATURE_DARK_MODE: /* Supports Dark Mode user interface */ + case DW_FEATURE_NOTIFICATION: /* Supports sending system notifications */ + case DW_FEATURE_UTF8_UNICODE: /* Supports UTF8 encoded Unicode text */ case DW_FEATURE_MLE_AUTO_COMPLETE: /* Supports auto completion in Multi-line Edit boxes */ case DW_FEATURE_MLE_WORD_WRAP: /* Supports word wrapping in Multi-line Edit boxes */ case DW_FEATURE_CONTAINER_STRIPE: /* Supports striped line display in container widgets */ - case DW_FEATURE_MDI: /* Supports Multiple Document Interface window frame */ - case DW_FEATURE_NOTEBOOK_STATUS_TEXT: /* Supports status text area on notebook/tabbed controls */ - case DW_FEATURE_NOTIFICATION: /* Supports sending system notifications */ - case DW_FEATURE_UTF8_UNICODE: /* Supports UTF8 encoded Unicode text */ - case DW_FEATURE_MLE_RICH_EDIT: /* Supports Rich Edit based MLE control (Windows) */ - case DW_FEATURE_TASK_BAR: /* Supports icons in the taskbar or similar system widget */ - case DW_FEATURE_TREE: .* Supports the Tree Widget */ return DW_FEATURE_ENABLED; -#endif default: return DW_FEATURE_UNSUPPORTED; } @@ -4990,24 +4989,15 @@ switch(feature) { /* These features are supported but not configurable */ -#if 0 case DW_FEATURE_HTML: /* Supports the HTML Widget */ case DW_FEATURE_HTML_RESULT: /* Supports the DW_SIGNAL_HTML_RESULT callback */ - case DW_FEATURE_WINDOW_BORDER: /* Supports custom window border sizes */ - case DW_FEATURE_WINDOW_TRANSPARENCY: /* Supports window frame transparency */ case DW_FEATURE_DARK_MODE: /* Supports Dark Mode user interface */ + case DW_FEATURE_NOTIFICATION: /* Supports sending system notifications */ + case DW_FEATURE_UTF8_UNICODE: /* Supports UTF8 encoded Unicode text */ case DW_FEATURE_MLE_AUTO_COMPLETE: /* Supports auto completion in Multi-line Edit boxes */ case DW_FEATURE_MLE_WORD_WRAP: /* Supports word wrapping in Multi-line Edit boxes */ case DW_FEATURE_CONTAINER_STRIPE: /* Supports striped line display in container widgets */ - case DW_FEATURE_MDI: /* Supports Multiple Document Interface window frame */ - case DW_FEATURE_NOTEBOOK_STATUS_TEXT: /* Supports status text area on notebook/tabbed controls */ - case DW_FEATURE_NOTIFICATION: /* Supports sending system notifications */ - case DW_FEATURE_UTF8_UNICODE: /* Supports UTF8 encoded Unicode text */ - case DW_FEATURE_MLE_RICH_EDIT: /* Supports Rich Edit based MLE control (Windows) */ - case DW_FEATURE_TASK_BAR: /* Supports icons in the taskbar or similar system widget */ - case DW_FEATURE_TREE: .* Supports the Tree Widget */ return DW_ERROR_GENERAL; -#endif /* These features are supported and configurable */ default: return DW_FEATURE_UNSUPPORTED;