comparison android/DWindows.kt @ 2773:f207b7ee177f

Android: Experimental Kotlin tree view implmentation, not enabled yet. Based on a Java implementation by Amr Hesham and modified for our use.
author bsmith@81767d24-ef19-dc11-ae90-00e081727c95
date Thu, 21 Apr 2022 20:58:08 +0000
parents 4f09bf72b391
children 94af460bb954
comparison
equal deleted inserted replaced
2772:af9dc4c2c0df 2773:f207b7ee177f
1 // (C) 2021-2022 Brian Smith <brian@dbsoft.org> 1 // (C) 2021-2022 Brian Smith <brian@dbsoft.org>
2 // (C) 2019 Anton Popov 2 // (C) 2019 Anton Popov (Color Picker)
3 // (C) 2022 Amr Hesham (Tree View)
3 package org.dbsoft.dwindows 4 package org.dbsoft.dwindows
4 5
5 import android.R 6 import android.R
6 import android.annotation.SuppressLint 7 import android.annotation.SuppressLint
7 import android.app.Activity 8 import android.app.Activity
15 import android.database.Cursor 16 import android.database.Cursor
16 import android.graphics.* 17 import android.graphics.*
17 import android.graphics.drawable.BitmapDrawable 18 import android.graphics.drawable.BitmapDrawable
18 import android.graphics.drawable.Drawable 19 import android.graphics.drawable.Drawable
19 import android.graphics.drawable.GradientDrawable 20 import android.graphics.drawable.GradientDrawable
21 import android.graphics.pdf.PdfDocument
20 import android.media.AudioManager 22 import android.media.AudioManager
21 import android.media.ToneGenerator 23 import android.media.ToneGenerator
22 import android.net.Uri 24 import android.net.Uri
23 import android.os.* 25 import android.os.*
26 import android.print.*
27 import android.print.pdf.PrintedPdfDocument
24 import android.provider.DocumentsContract 28 import android.provider.DocumentsContract
25 import android.provider.MediaStore 29 import android.provider.MediaStore
26 import android.text.InputFilter 30 import android.text.InputFilter
27 import android.text.InputFilter.LengthFilter 31 import android.text.InputFilter.LengthFilter
28 import android.text.InputType 32 import android.text.InputType
29 import android.text.method.PasswordTransformationMethod 33 import android.text.method.PasswordTransformationMethod
34 import android.util.*
35 import android.util.Base64
30 import android.view.* 36 import android.view.*
31 import android.view.View.OnTouchListener 37 import android.view.View.OnTouchListener
32 import android.view.ViewGroup
33 import android.view.inputmethod.EditorInfo 38 import android.view.inputmethod.EditorInfo
34 import android.webkit.WebView 39 import android.webkit.WebView
35 import android.webkit.WebViewClient 40 import android.webkit.WebViewClient
36 import android.widget.* 41 import android.widget.*
37 import android.widget.AdapterView.OnItemClickListener 42 import android.widget.AdapterView.OnItemClickListener
58 import java.io.IOException 63 import java.io.IOException
59 import java.util.* 64 import java.util.*
60 import java.util.concurrent.locks.ReentrantLock 65 import java.util.concurrent.locks.ReentrantLock
61 import java.util.zip.ZipEntry 66 import java.util.zip.ZipEntry
62 import java.util.zip.ZipFile 67 import java.util.zip.ZipFile
63 import android.content.Intent
64 import android.util.*
65 import android.util.Base64
66 import kotlin.math.* 68 import kotlin.math.*
67 import android.content.ContentUris 69
68 import android.content.DialogInterface 70 import androidx.annotation.NonNull
69 import android.graphics.pdf.PdfDocument 71
70 import android.print.* 72
71 import android.print.pdf.PrintedPdfDocument 73
72 import android.widget.Checkable 74
73 75 // Tree View section
74 import android.widget.LinearLayout 76 class DWTreeItem(value: Any, layoutId: Int) {
75 import android.graphics.BitmapFactory 77 private var value: Any
76 78 private var parent: DWTreeItem?
77 import android.graphics.Bitmap 79 private val children: LinkedList<DWTreeItem>
78 80 private val layoutId: Int
79 81 private var level: Int
80 82 private var isExpanded: Boolean
81 83 private var isSelected: Boolean
82 84 fun addChild(child: DWTreeItem) {
83 85 child.setParent(this)
84 86 child.setLevel(level + 1)
87 children.add(child)
88 updateNodeChildrenDepth(child)
89 }
90
91 fun setValue(value: Any) {
92 this.value = value
93 }
94
95 fun getValue(): Any {
96 return value
97 }
98
99 fun setParent(parent: DWTreeItem?) {
100 this.parent = parent
101 }
102
103 fun getParent(): DWTreeItem? {
104 return parent
105 }
106
107 fun getChildren(): LinkedList<DWTreeItem> {
108 return children
109 }
110
111 fun getLayoutId(): Int {
112 return layoutId
113 }
114
115 fun setLevel(level: Int) {
116 this.level = level
117 }
118
119 fun getLevel(): Int {
120 return level
121 }
122
123 fun setExpanded(expanded: Boolean) {
124 isExpanded = expanded
125 }
126
127 fun isExpanded(): Boolean {
128 return isExpanded
129 }
130
131 fun setSelected(selected: Boolean) {
132 isSelected = selected
133 }
134
135 fun isSelected(): Boolean {
136 return isSelected
137 }
138
139 private fun updateNodeChildrenDepth(node: DWTreeItem) {
140 if (node.getChildren().isEmpty()) return
141 for (child in node.getChildren()) {
142 child.setLevel(node.getLevel() + 1)
143 }
144 }
145
146 init {
147 this.value = value
148 parent = null
149 children = LinkedList()
150 this.layoutId = layoutId
151 level = 0
152 isExpanded = false
153 isSelected = false
154 }
155 }
156 class DWTreeItemManager {
157 // Collection to save the current tree nodes
158 private val rootsNodes: LinkedList<DWTreeItem>
159
160 // Get DWTreeItem from the current nodes by index
161 // @param index of node to get it
162 // @return DWTreeItem from by index from current tree nodes if exists
163 operator fun get(index: Int): DWTreeItem {
164 return rootsNodes[index]
165 }
166
167 // Add new node to the current tree nodes
168 // @param node to add it to the current tree nodes
169 // @return true of this node is added
170 fun addItem(node: DWTreeItem): Boolean {
171 return rootsNodes.add(node)
172 }
173
174 // Clear the current nodes and insert new nodes
175 // @param newNodes to update the current nodes with them
176 fun updateItems(newNodes: List<DWTreeItem>?) {
177 rootsNodes.clear()
178 rootsNodes.addAll(newNodes!!)
179 }
180
181 // Delete one node from the visible nodes
182 // @param node to delete it from the current nodes
183 // @return true of this node is deleted
184 fun removeItem(node: DWTreeItem): Boolean {
185 return rootsNodes.remove(node)
186 }
187
188 // Clear the current nodes
189 fun clearItems() {
190 rootsNodes.clear()
191 }
192
193 // Get the current number of visible nodes
194 // @return the size of visible nodes
195 fun size(): Int {
196 return rootsNodes.size
197 }
198
199 // Collapsing node and all of his children
200 // @param node The node to collapse it
201 // @return the index of this node if it exists in the list
202 fun collapseItem(node: DWTreeItem): Int {
203 val position = rootsNodes.indexOf(node)
204 if (position != -1 && node.isExpanded()) {
205 node.setExpanded(false)
206 val deletedParents: LinkedList<DWTreeItem> =
207 LinkedList(node.getChildren())
208 rootsNodes.removeAll(node.getChildren())
209 for (i in position + 1 until rootsNodes.size) {
210 val iNode: DWTreeItem = rootsNodes[i]
211 if (deletedParents.contains(iNode.getParent())) {
212 deletedParents.add(iNode)
213 deletedParents.addAll(iNode.getChildren())
214 }
215 }
216 rootsNodes.removeAll(deletedParents)
217 }
218 return position
219 }
220
221 // Expanding node and all of his children
222 // @param node The node to expand it
223 // @return the index of this node if it exists in the list
224 fun expandItem(node: DWTreeItem): Int {
225 val position = rootsNodes.indexOf(node)
226 if (position != -1 && !node.isExpanded()) {
227 node.setExpanded(true)
228 rootsNodes.addAll(position + 1, node.getChildren())
229 for (child in node.getChildren()) {
230 if (child.isExpanded()) updateExpandedItemChildren(child)
231 }
232 }
233 return position
234 }
235
236 // Update the list for expanded node
237 // to expand any child of his children that is already expanded before
238 // @param node that just expanded now
239 private fun updateExpandedItemChildren(node: DWTreeItem) {
240 val position = rootsNodes.indexOf(node)
241 if (position != -1 && node.isExpanded()) {
242 rootsNodes.addAll(position + 1, node.getChildren())
243 for (child in node.getChildren()) {
244 if (child.isExpanded()) updateExpandedItemChildren(child)
245 }
246 }
247 }
248
249 // @param node The node to collapse the branch of it
250 // @return the index of this node if it exists in the list
251 fun collapseItemBranch(node: DWTreeItem): Int {
252 val position = rootsNodes.indexOf(node)
253 if (position != -1 && node.isExpanded()) {
254 node.setExpanded(false)
255 for (child in node.getChildren()) {
256 if (!child.getChildren().isEmpty()) collapseItemBranch(child)
257 rootsNodes.remove(child)
258 }
259 }
260 return position
261 }
262
263 // Expanding node full branches
264 // @param node The node to expand the branch of it
265 // @return the index of this node if it exists in the list
266 fun expandItemBranch(node: DWTreeItem): Int {
267 val position = rootsNodes.indexOf(node)
268 if (position != -1 && !node.isExpanded()) {
269 node.setExpanded(true)
270 var index = position + 1
271 for (child in node.getChildren()) {
272 val before: Int = rootsNodes.size
273 rootsNodes.add(index, child)
274 expandItemBranch(child)
275 val after: Int = rootsNodes.size
276 val diff = after - before
277 index += diff
278 }
279 }
280 return position
281 }
282
283 // Expanding one node branch to until specific level
284 // @param node to expand branch of it until level
285 // @param level to expand node branches to it
286 fun expandItemToLevel(node: DWTreeItem, level: Int) {
287 if (node.getLevel() <= level) expandItem(node)
288 for (child in node.getChildren()) {
289 expandItemToLevel(child, level)
290 }
291 }
292
293 //Expanding all tree nodes branches to until specific level
294 //@param level to expand all nodes branches to it
295 fun expandItemsAtLevel(level: Int) {
296 for (i in 0 until rootsNodes.size) {
297 val node: DWTreeItem = rootsNodes[i]
298 expandItemToLevel(node, level)
299 }
300 }
301
302 // Collapsing all nodes in the tree with their children
303 fun collapseAll() {
304 val treeItems: MutableList<DWTreeItem> = LinkedList()
305 for (i in 0 until rootsNodes.size) {
306 val root: DWTreeItem = rootsNodes[i]
307 if (root.getLevel() === 0) {
308 collapseItemBranch(root)
309 treeItems.add(root)
310 } else {
311 root.setExpanded(false)
312 }
313 }
314 updateItems(treeItems)
315 }
316
317 // Expanding all nodes in the tree with their children
318 fun expandAll() {
319 for (i in 0 until rootsNodes.size) {
320 val root: DWTreeItem = rootsNodes[i]
321 expandItemBranch(root)
322 }
323 }
324
325 // Simple constructor
326 init {
327 rootsNodes = LinkedList()
328 }
329 }
330
331 open class DWTreeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
332 // Return the current DWTreeItem padding value
333 // @return The current padding value
334
335 // Modify the current node padding value
336 // @param padding the new padding value
337
338 // The default padding value for the DWTreeItem item
339 var nodePadding = 50
340
341 // Bind method that provide padding and bind DWTreeItem to the view list item
342 // @param node the current DWTreeItem
343 fun bindTreeItem(node: DWTreeItem) {
344 val padding: Int = node.getLevel() * nodePadding
345 itemView.setPadding(
346 padding,
347 itemView.paddingTop,
348 itemView.paddingRight,
349 itemView.paddingBottom
350 )
351 }
352 }
353
354 interface DWTreeViewHolderFactory {
355 // Provide a TreeViewHolder class depend on the current view
356 // @param view The list item view
357 // @param layout The layout xml file id for current view
358 // @return A TreeViewHolder instance
359 fun getTreeViewHolder(view: View?, layout: Int): DWTreeViewHolder
360 }
361
362 class DWTreeCustomViewHolder(itemView: View) : DWTreeViewHolder(itemView) {
363 fun bindTreeNode(node: DWTreeItem) {
364 super.bindTreeItem(node)
365 // Here you can bind your node and check if it selected or not
366 }
367 }
368
369 class DWTreeViewAdapter : RecyclerView.Adapter<DWTreeViewHolder> {
370 // Interface definition for a callback to be invoked when a DWTreeItem has been clicked and held.
371 interface OnTreeItemClickListener {
372 // Called when a DWTreeItem has been clicked.
373 // @param treeItem The current clicked node
374 // @param view The view that was clicked and held.
375 fun onTreeItemClick(treeItem: DWTreeItem?, view: View?)
376 }
377
378 // Interface definition for a callback to be invoked when a DWTreeItem has been clicked and held.
379 interface OnTreeItemLongClickListener {
380 // Called when a TreeItem has been clicked and held.
381 // @param treeItem The current clicked node
382 // @param view The view that was clicked and held.
383 // @return true if the callback consumed the long click, false otherwise.
384 fun onTreeItemLongClick(treeItem: DWTreeItem?, view: View?): Boolean
385 }
386
387 // Manager class for TreeItems to easily apply operations on them
388 // and to make it easy for testing and extending
389 private val treeItemManager: DWTreeItemManager
390
391 // A ViewHolder Factory to get DWTreeViewHolder object that mapped with layout
392 private val treeViewHolderFactory: DWTreeViewHolderFactory
393
394 // The current selected Tree Item
395 private var currentSelectedItem: DWTreeItem? = null
396
397 // Custom OnClickListener to be invoked when a DWTreeItem has been clicked.
398 private var treeItemClickListener: OnTreeItemClickListener? = null
399
400 // Custom OnLongClickListener to be invoked when a DWTreeItem has been clicked and hold.
401 private var treeItemLongClickListener: OnTreeItemLongClickListener? = null
402
403 // Simple constructor
404 // @param factory a View Holder Factory mapped with layout id's
405 constructor(factory: DWTreeViewHolderFactory) {
406 treeViewHolderFactory = factory
407 treeItemManager = DWTreeItemManager()
408 }
409
410 // Constructor used to accept user custom DWTreeItemManager class
411 // @param factory a View Holder Factory mapped with layout id's
412 // @param manager a custom tree node manager class
413 constructor(factory: DWTreeViewHolderFactory, manager: DWTreeItemManager) {
414 treeViewHolderFactory = factory
415 treeItemManager = manager
416 }
417
418 override fun onCreateViewHolder(parent: ViewGroup, layoutId: Int): DWTreeViewHolder {
419 val view = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
420 return treeViewHolderFactory.getTreeViewHolder(view, layoutId)
421 }
422
423 override fun onBindViewHolder(holder: DWTreeViewHolder, position: Int) {
424 val currentNode: DWTreeItem = treeItemManager.get(position)
425 holder.bindTreeItem(currentNode)
426 holder.itemView.setOnClickListener { v ->
427 // Handle node selection
428 currentNode.setSelected(true)
429 currentSelectedItem?.setSelected(false)
430 currentSelectedItem = currentNode
431
432 // Handle node expand and collapse event
433 if (!currentNode.getChildren().isEmpty()) {
434 val isNodeExpanded: Boolean = currentNode.isExpanded()
435 if (isNodeExpanded) collapseNode(currentNode) else expandNode(currentNode)
436 currentNode.setExpanded(!isNodeExpanded)
437 }
438 notifyDataSetChanged()
439
440 // Handle DWTreeItem click listener event
441 if (treeItemClickListener != null) treeItemClickListener!!.onTreeItemClick(
442 currentNode,
443 v
444 )
445 }
446
447 // Handle DWTreeItem long click listener event
448 holder.itemView.setOnLongClickListener { v ->
449 if (treeItemLongClickListener != null) {
450 return@setOnLongClickListener treeItemLongClickListener!!.onTreeItemLongClick(
451 currentNode,
452 v
453 )
454 }
455 true
456 }
457 }
458
459 override fun getItemViewType(position: Int): Int {
460 return treeItemManager.get(position).getLayoutId()
461 }
462
463 override fun getItemCount(): Int {
464 return treeItemManager.size()
465 }
466
467 // Collapsing node and all of his children
468 // @param node The node to collapse it
469 fun collapseNode(node: DWTreeItem) {
470 val position: Int = treeItemManager.collapseItem(node)
471 if (position != -1) {
472 notifyDataSetChanged()
473 }
474 }
475
476 // Expanding node and all of his children
477 // @param node The node to expand it
478 fun expandNode(node: DWTreeItem) {
479 val position: Int = treeItemManager.expandItem(node)
480 if (position != -1) {
481 notifyDataSetChanged()
482 }
483 }
484
485 // Collapsing full node branches
486 // @param node The node to collapse it
487 fun collapseNodeBranch(node: DWTreeItem) {
488 treeItemManager.collapseItemBranch(node)
489 notifyDataSetChanged()
490 }
491
492 // Expanding node full branches
493 // @param node The node to expand it
494 fun expandNodeBranch(node: DWTreeItem) {
495 treeItemManager.expandItemBranch(node)
496 notifyDataSetChanged()
497 }
498
499 // Expanding one node branch to until specific level
500 // @param node to expand branch of it until level
501 // @param level to expand node branches to it
502 fun expandNodeToLevel(node: DWTreeItem, level: Int) {
503 treeItemManager.expandItemToLevel(node, level)
504 notifyDataSetChanged()
505 }
506
507 // Expanding all tree nodes branches to until specific level
508 // @param level to expand all nodes branches to it
509 fun expandNodesAtLevel(level: Int) {
510 treeItemManager.expandItemsAtLevel(level)
511 notifyDataSetChanged()
512 }
513
514 // Collapsing all nodes in the tree with their children
515 fun collapseAll() {
516 treeItemManager.collapseAll()
517 notifyDataSetChanged()
518 }
519
520 // Expanding all nodes in the tree with their children
521 fun expandAll() {
522 treeItemManager.expandAll()
523 notifyDataSetChanged()
524 }
525
526 // Update the list of tree nodes
527 // @param treeItems The new tree nodes
528 fun updateTreeItems(treeItems: List<DWTreeItem>) {
529 treeItemManager.updateItems(treeItems)
530 notifyItemRangeInserted(0, treeItems.size)
531 }
532
533 // Register a callback to be invoked when this DWTreeItem is clicked
534 // @param listener The callback that will run
535 fun setTreeItemClickListener(listener: OnTreeItemClickListener?) {
536 treeItemClickListener = listener
537 }
538
539 // Register a callback to be invoked when this DWTreeItem is clicked and held
540 // @param listener The callback that will run
541 fun setTreeItemLongClickListener(listener: OnTreeItemLongClickListener?) {
542 treeItemLongClickListener = listener
543 }
544
545 // @return The current selected DWTreeItem
546 val selectedNode: DWTreeItem?
547 get() = currentSelectedItem
548 }
85 549
86 // Color Wheel section 550 // Color Wheel section
87 private val HUE_COLORS = intArrayOf( 551 private val HUE_COLORS = intArrayOf(
88 Color.RED, 552 Color.RED,
89 Color.YELLOW, 553 Color.YELLOW,
4022 combobox!!.inputType = (InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) 4486 combobox!!.inputType = (InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)
4023 combobox!!.setText(text) 4487 combobox!!.setText(text)
4024 } 4488 }
4025 return combobox 4489 return combobox
4026 } 4490 }
4491
4492 /*fun treeNew(cid: Int): RecyclerView?
4493 {
4494 var tree: RecyclerView? = null
4495
4496 waitOnUiThread {
4497 tree = RecyclerView(this)
4498 if(tree != null) {
4499 val factory = DWTreeViewHolderFactory { v: View?, layout: Int -> DWTreeCustomViewHolder(v!!) }
4500 val treeViewAdapter = DWTreeViewAdapter(factory)
4501 tree!!.id = cid
4502 tree!!.adapter = treeViewAdapter
4503 }
4504 }
4505 return tree
4506 }*/
4027 4507
4028 fun containerNew(cid: Int, multi: Int): ListView? 4508 fun containerNew(cid: Int, multi: Int): ListView?
4029 { 4509 {
4030 var cont: ListView? = null 4510 var cont: ListView? = null
4031 4511