-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
91b4271
commit 45d047a
Showing
20 changed files
with
550 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
48 changes: 48 additions & 0 deletions
48
filetree/src/main/kotlin/com/zyron/filetree/callback/FileTreeDiffCallback.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/** | ||
* Copyright 2024 Zyron Official. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.zyron.filetree.callback | ||
|
||
import androidx.recyclerview.widget.DiffUtil | ||
import com.zyron.filetree.datamodel.FileTreeNode | ||
|
||
/** | ||
* A DiffUtil.ItemCallback implementation for comparing FileTreeNode objects. | ||
*/ | ||
class FileTreeDiffCallback : DiffUtil.ItemCallback<FileTreeNode>() { | ||
|
||
/** | ||
* Determines if two FileTreeNode items represent the same file by comparing their absolute paths. | ||
* | ||
* @param oldItem The previous FileTreeNode item. | ||
* @param newItem The new FileTreeNode item. | ||
* @return True if the two items represent the same file, false otherwise. | ||
*/ | ||
override fun areItemsTheSame(oldItem: FileTreeNode, newItem: FileTreeNode): Boolean { | ||
return oldItem.file.absolutePath == newItem.file.absolutePath | ||
} | ||
|
||
/** | ||
* Determines if the contents of two FileTreeNode items are the same. | ||
* | ||
* @param oldItem The previous FileTreeNode item. | ||
* @param newItem The new FileTreeNode item. | ||
* @return True if the contents of the two items are the same, false otherwise. | ||
*/ | ||
override fun areContentsTheSame(oldItem: FileTreeNode, newItem: FileTreeNode): Boolean { | ||
return oldItem == newItem | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
filetree/src/main/kotlin/com/zyron/filetree/datamodel/FileTreeNode.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/** | ||
* Copyright 2024 Zyron Official. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.zyron.filetree.datamodel | ||
|
||
import java.io.File | ||
|
||
/** | ||
* Represents a node in a file tree, corresponding to a file or directory. | ||
* | ||
* @property file The file or directory represented by this node. | ||
* @property parent The parent node of this node in the file tree. | ||
* @property level The depth level of this node in the file tree. | ||
*/ | ||
data class FileTreeNode(var file: File, var parent: FileTreeNode? = null, var level: Int = 0) { | ||
var isExpanded: Boolean = false | ||
var childrenStartIndex: Int = 0 | ||
var childrenEndIndex: Int = 0 | ||
var childrenLoaded: Boolean = false | ||
|
||
/** | ||
* Sorts the children of this node, separating directories from files and sorting them alphabetically. | ||
* | ||
* @return A list of FileTreeNode objects representing the sorted children. | ||
*/ | ||
fun sortNode(): List<FileTreeNode> { | ||
val children = file.listFiles() | ||
?.asSequence() | ||
?.partition { it.isDirectory } | ||
?.let { (directories, files) -> | ||
(directories.sortedBy { it.name.lowercase() } + files.sortedBy { it.name.lowercase() }) | ||
} ?: emptyList() | ||
return children.map { childFile -> | ||
FileTreeNode(file = childFile, parent = this, level = level + 1) | ||
} | ||
} | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (other == null || javaClass != other.javaClass) return false | ||
other as FileTreeNode | ||
return file.absolutePath == other.file.absolutePath | ||
} | ||
|
||
override fun hashCode(): Int { | ||
return file.absolutePath.hashCode() | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
filetree/src/main/kotlin/com/zyron/filetree/events/FileTreeEventListener.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/** | ||
* Copyright 2024 Zyron Official. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.zyron.filetree.events | ||
|
||
import java.io.File | ||
|
||
/** | ||
* Interface to listen for events within a file tree, including clicks and updates. | ||
*/ | ||
interface FileTreeEventListener { | ||
|
||
/** | ||
* Called when a file is clicked. | ||
* | ||
* @param file The file that was clicked. | ||
*/ | ||
fun onFileClick(file: File) | ||
|
||
/** | ||
* Called when a folder is clicked. | ||
* | ||
* @param folder The folder that was clicked. | ||
*/ | ||
fun onFolderClick(folder: File) | ||
|
||
/** | ||
* Called when a file is long-clicked. | ||
* | ||
* @param file The file that was long-clicked. | ||
* @return True if the long-click event was handled, false otherwise. | ||
*/ | ||
fun onFileLongClick(file: File): Boolean | ||
|
||
/** | ||
* Called when a folder is long-clicked. | ||
* | ||
* @param folder The folder that was long-clicked. | ||
* @return True if the long-click event was handled, false otherwise. | ||
*/ | ||
fun onFolderLongClick(folder: File): Boolean | ||
|
||
/** | ||
* Called when the file tree view is updated. | ||
* | ||
* @param startPosition The starting position of the update. | ||
* @param itemCount The number of items updated. | ||
*/ | ||
fun onFileTreeViewUpdated(startPosition: Int, itemCount: Int) | ||
} |
127 changes: 127 additions & 0 deletions
127
filetree/src/main/kotlin/com/zyron/filetree/map/ConcurrentFileMap.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/** | ||
* Copyright 2024 Zyron Official. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.zyron.filetree.map | ||
|
||
import com.zyron.filetree.datamodel.FileTreeNode | ||
import java.util.concurrent.ConcurrentHashMap | ||
import java.util.concurrent.Executors | ||
import java.util.concurrent.Future | ||
import java.util.concurrent.ScheduledExecutorService | ||
import java.util.concurrent.TimeUnit | ||
|
||
/** | ||
* Manages a cache of file nodes for efficient access and manipulation using concurrency. | ||
* | ||
* @param nodes A list of root nodes to be processed and cached. | ||
* @param maxSize The maximum size of the cache before trimming. | ||
*/ | ||
class ConcurrentFileMap(private val nodes: List<FileTreeNode>, private val maxSize: Int = 100) : Runnable { | ||
|
||
companion object { | ||
val concurrentFileMap: MutableMap<FileTreeNode, List<FileTreeNode>> = ConcurrentHashMap() | ||
} | ||
|
||
private val cache = ConcurrentHashMap<FileTreeNode, List<FileTreeNode>>(maxSize) | ||
private val executor: ScheduledExecutorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors()) | ||
|
||
init { | ||
// Schedule cache trimming at fixed intervals | ||
executor.scheduleAtFixedRate(::trimCache, 10, 10, TimeUnit.SECONDS) | ||
} | ||
|
||
/** | ||
* Retrieves a list of child nodes for a given file node from the cache. | ||
* | ||
* @param node The parent file node. | ||
* @return A list of child file nodes, or null if the node is not in the cache. | ||
*/ | ||
fun get(node: FileTreeNode): List<FileTreeNode>? { | ||
return cache[node] | ||
} | ||
|
||
/** | ||
* Adds a file node and its children to the cache. | ||
* | ||
* @param node The parent file node. | ||
* @param result The list of child file nodes. | ||
*/ | ||
fun put(node: FileTreeNode, result: List<FileTreeNode>) { | ||
cache[node] = result | ||
if (cache.size > maxSize) { | ||
trimCache() | ||
} | ||
} | ||
|
||
/** | ||
* Clears the entire cache. | ||
*/ | ||
fun clear() { | ||
cache.clear() | ||
} | ||
|
||
/** | ||
* Processes a list of file nodes asynchronously to populate the cache. | ||
* | ||
* @param nodes The list of file nodes to process. | ||
*/ | ||
private fun processNodes(nodes: List<FileTreeNode>) { | ||
val futures = mutableListOf<Future<*>>() | ||
for (node in nodes) { | ||
val future = executor.submit { | ||
val nodeList = node.sortNode() | ||
put(node, nodeList) | ||
processNodes(nodeList) | ||
} | ||
futures.add(future) | ||
} | ||
futures.forEach { it.get() } | ||
} | ||
|
||
/** | ||
* Trims the cache to the specified maximum size by removing excess entries. | ||
*/ | ||
private fun trimCache() { | ||
if (cache.size > maxSize) { | ||
val keysToRemove = cache.keys.take(cache.size - maxSize) | ||
for (key in keysToRemove) { | ||
cache.remove(key) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Entry point for processing nodes when this Runnable is executed. | ||
*/ | ||
override fun run() { | ||
processNodes(nodes) | ||
} | ||
|
||
/** | ||
* Shuts down the executor service and cancels all running tasks. | ||
*/ | ||
fun shutdown() { | ||
executor.shutdown() | ||
try { | ||
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { | ||
executor.shutdownNow() | ||
} | ||
} catch (ex: InterruptedException) { | ||
executor.shutdownNow() | ||
Thread.currentThread().interrupt() | ||
} | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
filetree/src/main/kotlin/com/zyron/filetree/map/FileMapManager.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/** | ||
* Copyright 2024 Zyron Official. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.zyron.filetree.map | ||
|
||
import com.zyron.filetree.datamodel.FileTreeNode | ||
import java.util.concurrent.Executors | ||
import java.util.concurrent.Future | ||
import android.util.Log | ||
|
||
/** | ||
* Manages the mapping of file nodes using a single-threaded executor. | ||
*/ | ||
object FileMapManager { | ||
private var fileCacheFuture: Future<*>? = null | ||
private val executor = Executors.newSingleThreadExecutor() | ||
|
||
/** | ||
* Stops the current file mapping task, if one is running. | ||
*/ | ||
fun stopFileMapping() { | ||
fileCacheFuture?.cancel(true) | ||
fileCacheFuture = null | ||
Log.i(this::class.java.simpleName, "FileMapping stopped") | ||
} | ||
|
||
/** | ||
* Starts the file mapping process on a separate thread. | ||
* | ||
* @param nodes The list of root nodes to process. | ||
* @param priority Optional priority setting for the mapping thread. | ||
*/ | ||
fun startFileMapping(nodes: List<FileTreeNode>, priority: Int? = null) { | ||
if (fileCacheFuture == null) { | ||
fileCacheFuture = executor.submit { | ||
try { | ||
val fileCache = ConcurrentFileMap(nodes) | ||
fileCache.run() | ||
} catch (e: InterruptedException) { | ||
Thread.currentThread().interrupt() | ||
} catch (e: Exception) { | ||
Log.e(this::class.java.simpleName, "Error in FileMapping", e) | ||
} | ||
}.apply { | ||
priority?.let { (this as Thread).priority = it } | ||
} | ||
Log.i(this::class.java.simpleName, "FileMapping started") | ||
} else { | ||
Log.e(this::class.java.simpleName, "FileMapping is already running; this might cause issues") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<set xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:interpolator="@android:interpolator/accelerate_decelerate" | ||
android:duration="500"> | ||
</set> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" | ||
android:animationOrder="normal" | ||
android:delay="10%" | ||
android:animation="@anim/default_anim" > | ||
</layoutAnimation> |
Oops, something went wrong.