Mercurial > dwindows
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 |