summaryrefslogtreecommitdiffstats
path: root/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt
blob: f8abae445fd3007808e8f14312df2adbccf9df26 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

package org.yuzu.yuzu_emu.utils

import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import java.io.File
import java.util.*

class DocumentsTree {
    private var root: DocumentsNode? = null

    fun setRoot(rootUri: Uri?) {
        root = null
        root = DocumentsNode()
        root!!.uri = rootUri
        root!!.isDirectory = true
    }

    fun openContentUri(filepath: String, openMode: String?): Int {
        val node = resolvePath(filepath) ?: return -1
        return FileUtil.openContentUri(YuzuApplication.appContext, node.uri.toString(), openMode)
    }

    fun getFileSize(filepath: String): Long {
        val node = resolvePath(filepath)
        return if (node == null || node.isDirectory) {
            0
        } else FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
    }

    fun exists(filepath: String): Boolean {
        return resolvePath(filepath) != null
    }

    fun isDirectory(filepath: String): Boolean {
        val node = resolvePath(filepath)
        return node != null && node.isDirectory
    }

    private fun resolvePath(filepath: String): DocumentsNode? {
        val tokens = StringTokenizer(filepath, File.separator, false)
        var iterator = root
        while (tokens.hasMoreTokens()) {
            val token = tokens.nextToken()
            if (token.isEmpty()) continue
            iterator = find(iterator, token)
            if (iterator == null) return null
        }
        return iterator
    }

    private fun find(parent: DocumentsNode?, filename: String): DocumentsNode? {
        if (parent!!.isDirectory && !parent.loaded) {
            structTree(parent)
        }
        return parent.children[filename]
    }

    /**
     * Construct current level directory tree
     * @param parent parent node of this level
     */
    private fun structTree(parent: DocumentsNode) {
        val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!)
        for (document in documents) {
            val node = DocumentsNode(document)
            node.parent = parent
            parent.children[node.name] = node
        }
        parent.loaded = true
    }

    private class DocumentsNode {
        var parent: DocumentsNode? = null
        val children: MutableMap<String?, DocumentsNode> = HashMap()
        var name: String? = null
        var uri: Uri? = null
        var loaded = false
        var isDirectory = false

        constructor()
        constructor(document: MinimalDocumentFile) {
            name = document.filename
            uri = document.uri
            isDirectory = document.isDirectory
            loaded = !isDirectory
        }

        private constructor(document: DocumentFile, isCreateDir: Boolean) {
            name = document.name
            uri = document.uri
            isDirectory = isCreateDir
            loaded = true
        }

        private fun rename(name: String) {
            if (parent == null) {
                return
            }
            parent!!.children.remove(this.name)
            this.name = name
            parent!!.children[name] = this
        }
    }

    companion object {
        fun isNativePath(path: String): Boolean {
            return if (path.isNotEmpty()) {
                path[0] == '/'
            } else false
        }
    }
}