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 }
}
|