Skip to content

Commit 45d047a

Browse files
Added untracked files
1 parent 91b4271 commit 45d047a

20 files changed

Lines changed: 550 additions & 0 deletions

.androidide/editor/openedFiles.json.bak

Whitespace-only changes.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright 2024 Zyron Official.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.zyron.filetree.callback
18+
19+
import androidx.recyclerview.widget.DiffUtil
20+
import com.zyron.filetree.datamodel.FileTreeNode
21+
22+
/**
23+
* A DiffUtil.ItemCallback implementation for comparing FileTreeNode objects.
24+
*/
25+
class FileTreeDiffCallback : DiffUtil.ItemCallback<FileTreeNode>() {
26+
27+
/**
28+
* Determines if two FileTreeNode items represent the same file by comparing their absolute paths.
29+
*
30+
* @param oldItem The previous FileTreeNode item.
31+
* @param newItem The new FileTreeNode item.
32+
* @return True if the two items represent the same file, false otherwise.
33+
*/
34+
override fun areItemsTheSame(oldItem: FileTreeNode, newItem: FileTreeNode): Boolean {
35+
return oldItem.file.absolutePath == newItem.file.absolutePath
36+
}
37+
38+
/**
39+
* Determines if the contents of two FileTreeNode items are the same.
40+
*
41+
* @param oldItem The previous FileTreeNode item.
42+
* @param newItem The new FileTreeNode item.
43+
* @return True if the contents of the two items are the same, false otherwise.
44+
*/
45+
override fun areContentsTheSame(oldItem: FileTreeNode, newItem: FileTreeNode): Boolean {
46+
return oldItem == newItem
47+
}
48+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Copyright 2024 Zyron Official.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.zyron.filetree.datamodel
18+
19+
import java.io.File
20+
21+
/**
22+
* Represents a node in a file tree, corresponding to a file or directory.
23+
*
24+
* @property file The file or directory represented by this node.
25+
* @property parent The parent node of this node in the file tree.
26+
* @property level The depth level of this node in the file tree.
27+
*/
28+
data class FileTreeNode(var file: File, var parent: FileTreeNode? = null, var level: Int = 0) {
29+
var isExpanded: Boolean = false
30+
var childrenStartIndex: Int = 0
31+
var childrenEndIndex: Int = 0
32+
var childrenLoaded: Boolean = false
33+
34+
/**
35+
* Sorts the children of this node, separating directories from files and sorting them alphabetically.
36+
*
37+
* @return A list of FileTreeNode objects representing the sorted children.
38+
*/
39+
fun sortNode(): List<FileTreeNode> {
40+
val children = file.listFiles()
41+
?.asSequence()
42+
?.partition { it.isDirectory }
43+
?.let { (directories, files) ->
44+
(directories.sortedBy { it.name.lowercase() } + files.sortedBy { it.name.lowercase() })
45+
} ?: emptyList()
46+
return children.map { childFile ->
47+
FileTreeNode(file = childFile, parent = this, level = level + 1)
48+
}
49+
}
50+
51+
override fun equals(other: Any?): Boolean {
52+
if (this === other) return true
53+
if (other == null || javaClass != other.javaClass) return false
54+
other as FileTreeNode
55+
return file.absolutePath == other.file.absolutePath
56+
}
57+
58+
override fun hashCode(): Int {
59+
return file.absolutePath.hashCode()
60+
}
61+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright 2024 Zyron Official.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.zyron.filetree.events
18+
19+
import java.io.File
20+
21+
/**
22+
* Interface to listen for events within a file tree, including clicks and updates.
23+
*/
24+
interface FileTreeEventListener {
25+
26+
/**
27+
* Called when a file is clicked.
28+
*
29+
* @param file The file that was clicked.
30+
*/
31+
fun onFileClick(file: File)
32+
33+
/**
34+
* Called when a folder is clicked.
35+
*
36+
* @param folder The folder that was clicked.
37+
*/
38+
fun onFolderClick(folder: File)
39+
40+
/**
41+
* Called when a file is long-clicked.
42+
*
43+
* @param file The file that was long-clicked.
44+
* @return True if the long-click event was handled, false otherwise.
45+
*/
46+
fun onFileLongClick(file: File): Boolean
47+
48+
/**
49+
* Called when a folder is long-clicked.
50+
*
51+
* @param folder The folder that was long-clicked.
52+
* @return True if the long-click event was handled, false otherwise.
53+
*/
54+
fun onFolderLongClick(folder: File): Boolean
55+
56+
/**
57+
* Called when the file tree view is updated.
58+
*
59+
* @param startPosition The starting position of the update.
60+
* @param itemCount The number of items updated.
61+
*/
62+
fun onFileTreeViewUpdated(startPosition: Int, itemCount: Int)
63+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* Copyright 2024 Zyron Official.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.zyron.filetree.map
18+
19+
import com.zyron.filetree.datamodel.FileTreeNode
20+
import java.util.concurrent.ConcurrentHashMap
21+
import java.util.concurrent.Executors
22+
import java.util.concurrent.Future
23+
import java.util.concurrent.ScheduledExecutorService
24+
import java.util.concurrent.TimeUnit
25+
26+
/**
27+
* Manages a cache of file nodes for efficient access and manipulation using concurrency.
28+
*
29+
* @param nodes A list of root nodes to be processed and cached.
30+
* @param maxSize The maximum size of the cache before trimming.
31+
*/
32+
class ConcurrentFileMap(private val nodes: List<FileTreeNode>, private val maxSize: Int = 100) : Runnable {
33+
34+
companion object {
35+
val concurrentFileMap: MutableMap<FileTreeNode, List<FileTreeNode>> = ConcurrentHashMap()
36+
}
37+
38+
private val cache = ConcurrentHashMap<FileTreeNode, List<FileTreeNode>>(maxSize)
39+
private val executor: ScheduledExecutorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors())
40+
41+
init {
42+
// Schedule cache trimming at fixed intervals
43+
executor.scheduleAtFixedRate(::trimCache, 10, 10, TimeUnit.SECONDS)
44+
}
45+
46+
/**
47+
* Retrieves a list of child nodes for a given file node from the cache.
48+
*
49+
* @param node The parent file node.
50+
* @return A list of child file nodes, or null if the node is not in the cache.
51+
*/
52+
fun get(node: FileTreeNode): List<FileTreeNode>? {
53+
return cache[node]
54+
}
55+
56+
/**
57+
* Adds a file node and its children to the cache.
58+
*
59+
* @param node The parent file node.
60+
* @param result The list of child file nodes.
61+
*/
62+
fun put(node: FileTreeNode, result: List<FileTreeNode>) {
63+
cache[node] = result
64+
if (cache.size > maxSize) {
65+
trimCache()
66+
}
67+
}
68+
69+
/**
70+
* Clears the entire cache.
71+
*/
72+
fun clear() {
73+
cache.clear()
74+
}
75+
76+
/**
77+
* Processes a list of file nodes asynchronously to populate the cache.
78+
*
79+
* @param nodes The list of file nodes to process.
80+
*/
81+
private fun processNodes(nodes: List<FileTreeNode>) {
82+
val futures = mutableListOf<Future<*>>()
83+
for (node in nodes) {
84+
val future = executor.submit {
85+
val nodeList = node.sortNode()
86+
put(node, nodeList)
87+
processNodes(nodeList)
88+
}
89+
futures.add(future)
90+
}
91+
futures.forEach { it.get() }
92+
}
93+
94+
/**
95+
* Trims the cache to the specified maximum size by removing excess entries.
96+
*/
97+
private fun trimCache() {
98+
if (cache.size > maxSize) {
99+
val keysToRemove = cache.keys.take(cache.size - maxSize)
100+
for (key in keysToRemove) {
101+
cache.remove(key)
102+
}
103+
}
104+
}
105+
106+
/**
107+
* Entry point for processing nodes when this Runnable is executed.
108+
*/
109+
override fun run() {
110+
processNodes(nodes)
111+
}
112+
113+
/**
114+
* Shuts down the executor service and cancels all running tasks.
115+
*/
116+
fun shutdown() {
117+
executor.shutdown()
118+
try {
119+
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
120+
executor.shutdownNow()
121+
}
122+
} catch (ex: InterruptedException) {
123+
executor.shutdownNow()
124+
Thread.currentThread().interrupt()
125+
}
126+
}
127+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright 2024 Zyron Official.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.zyron.filetree.map
18+
19+
import com.zyron.filetree.datamodel.FileTreeNode
20+
import java.util.concurrent.Executors
21+
import java.util.concurrent.Future
22+
import android.util.Log
23+
24+
/**
25+
* Manages the mapping of file nodes using a single-threaded executor.
26+
*/
27+
object FileMapManager {
28+
private var fileCacheFuture: Future<*>? = null
29+
private val executor = Executors.newSingleThreadExecutor()
30+
31+
/**
32+
* Stops the current file mapping task, if one is running.
33+
*/
34+
fun stopFileMapping() {
35+
fileCacheFuture?.cancel(true)
36+
fileCacheFuture = null
37+
Log.i(this::class.java.simpleName, "FileMapping stopped")
38+
}
39+
40+
/**
41+
* Starts the file mapping process on a separate thread.
42+
*
43+
* @param nodes The list of root nodes to process.
44+
* @param priority Optional priority setting for the mapping thread.
45+
*/
46+
fun startFileMapping(nodes: List<FileTreeNode>, priority: Int? = null) {
47+
if (fileCacheFuture == null) {
48+
fileCacheFuture = executor.submit {
49+
try {
50+
val fileCache = ConcurrentFileMap(nodes)
51+
fileCache.run()
52+
} catch (e: InterruptedException) {
53+
Thread.currentThread().interrupt()
54+
} catch (e: Exception) {
55+
Log.e(this::class.java.simpleName, "Error in FileMapping", e)
56+
}
57+
}.apply {
58+
priority?.let { (this as Thread).priority = it }
59+
}
60+
Log.i(this::class.java.simpleName, "FileMapping started")
61+
} else {
62+
Log.e(this::class.java.simpleName, "FileMapping is already running; this might cause issues")
63+
}
64+
}
65+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<set xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:interpolator="@android:interpolator/accelerate_decelerate"
4+
android:duration="500">
5+
</set>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:animationOrder="normal"
3+
android:delay="10%"
4+
android:animation="@anim/default_anim" >
5+
</layoutAnimation>

0 commit comments

Comments
 (0)