aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/drive/DriveSource.kt
blob: 69077faafc0a9b34546a394b36bb2f2921e7eb72 (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
package org.meshbay.client.drive

import android.content.Context
import android.net.Uri
import android.provider.DocumentsContract
import android.provider.DocumentsContract.Document
import java.io.InputStream

/**
 * The files of the folders the person chose, read through the Storage Access
 * Framework: each folder was granted through the system's own picker, which
 * is all the access this needs (no storage permission, which Play keeps for a
 * few kinds of application).
 */
class DriveSource(private val context: Context) {

    /** Every file under `tree`, its sub-folders included; hidden ones (`.x`) and their contents skipped. */
    fun files(tree: Uri): List<DriveFile> {
        val out = ArrayList<DriveFile>()
        val root = DocumentsContract.getTreeDocumentId(tree)
        val pending = ArrayDeque(listOf(root to ""))
        val cols = arrayOf(Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_MIME_TYPE,
                           Document.COLUMN_SIZE, Document.COLUMN_LAST_MODIFIED)
        while (pending.isNotEmpty()) {
            val (parent, sub) = pending.removeFirst()
            val children = DocumentsContract.buildChildDocumentsUriUsingTree(tree, parent)
            context.contentResolver.query(children, cols, null, null, null)?.use { c ->
                while (c.moveToNext()) {
                    val id = c.getString(0) ?: continue
                    val name = c.getString(1) ?: continue
                    if (name.startsWith('.')) continue
                    val mime = c.getString(2) ?: ""
                    if (mime == Document.MIME_TYPE_DIR) {
                        pending.addLast(id to (if (sub.isEmpty()) name else "$sub/$name"))
                    } else {
                        out += DriveFile(tree.toString(), id, sub, name,
                                         if (c.isNull(3)) 0 else c.getLong(3),
                                         if (c.isNull(4)) 0 else c.getLong(4), mime)
                    }
                }
            }
        }
        return out
    }

    fun open(f: DriveFile): InputStream? = context.contentResolver.openInputStream(
        DocumentsContract.buildDocumentUriUsingTree(Uri.parse(f.tree), f.docId))

    /** The chosen folder's own name, as the picker showed it. */
    fun nameOf(tree: Uri): String? = try {
        val doc = DocumentsContract.buildDocumentUriUsingTree(tree, DocumentsContract.getTreeDocumentId(tree))
        context.contentResolver.query(doc, arrayOf(Document.COLUMN_DISPLAY_NAME), null, null, null)
            ?.use { c -> if (c.moveToFirst()) c.getString(0) else null }
    } catch (e: Exception) { null }
}