Skip to content

Commit 2b1ebe5

Browse files
committed
feat(android): add SAF read access
Fixes #15
1 parent 036c3c9 commit 2b1ebe5

File tree

1 file changed

+23
-6
lines changed

1 file changed

+23
-6
lines changed

android/src/main/java/com/alpha0010/fs/FileAccessModule.kt

+23-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.alpha0010.fs
22

33
import android.content.ContentValues
4+
import android.net.Uri
45
import android.os.Environment
56
import android.os.StatFs
67
import android.provider.MediaStore
@@ -15,6 +16,7 @@ import okhttp3.Callback
1516
import java.io.File
1617
import java.io.FileOutputStream
1718
import java.io.IOException
19+
import java.io.InputStream
1820
import java.security.MessageDigest
1921

2022
class FileAccessModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
@@ -61,7 +63,7 @@ class FileAccessModule(reactContext: ReactApplicationContext) : ReactContextBase
6163
@ReactMethod
6264
fun concatFiles(source: String, target: String, promise: Promise) {
6365
try {
64-
File(source).inputStream().use { input ->
66+
openForReading(source).use { input ->
6567
FileOutputStream(File(target), true).use {
6668
promise.resolve(input.copyTo(it).toInt())
6769
}
@@ -75,7 +77,9 @@ class FileAccessModule(reactContext: ReactApplicationContext) : ReactContextBase
7577
fun cp(source: String, target: String, promise: Promise) {
7678
ioScope.launch {
7779
try {
78-
File(source).copyTo(File(target), overwrite = true)
80+
openForReading(source).use { input ->
81+
File(target).outputStream().use { input.copyTo(it) }
82+
}
7983
promise.resolve(null)
8084
} catch (e: Throwable) {
8185
promise.reject(e)
@@ -98,7 +102,7 @@ class FileAccessModule(reactContext: ReactApplicationContext) : ReactContextBase
98102
@ReactMethod
99103
fun cpExternal(source: String, targetName: String, dir: String, promise: Promise) {
100104
try {
101-
File(source).inputStream().use { input ->
105+
openForReading(source).use { input ->
102106
if (dir == "downloads") {
103107
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
104108
reactApplicationContext.contentResolver.insert(
@@ -254,7 +258,7 @@ class FileAccessModule(reactContext: ReactApplicationContext) : ReactContextBase
254258
fun hash(path: String, algorithm: String, promise: Promise) {
255259
try {
256260
val digest = MessageDigest.getInstance(algorithm)
257-
File(path).inputStream().use {
261+
openForReading(path).use {
258262
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
259263
var bytes = it.read(buffer)
260264
while (bytes >= 0) {
@@ -333,10 +337,11 @@ class FileAccessModule(reactContext: ReactApplicationContext) : ReactContextBase
333337
@ReactMethod
334338
fun readFile(path: String, encoding: String, promise: Promise) {
335339
try {
340+
val data = openForReading(path).use { it.readBytes() }
336341
if (encoding == "base64") {
337-
promise.resolve(Base64.encodeToString(File(path).readBytes(), Base64.DEFAULT))
342+
promise.resolve(Base64.encodeToString(data, Base64.DEFAULT))
338343
} else {
339-
promise.resolve(File(path).readText())
344+
promise.resolve(data.decodeToString())
340345
}
341346
} catch (e: Throwable) {
342347
promise.reject(e)
@@ -391,4 +396,16 @@ class FileAccessModule(reactContext: ReactApplicationContext) : ReactContextBase
391396
}
392397
}
393398
}
399+
400+
/**
401+
* Open a file. Supports both standard file system paths and Storage Access
402+
* Framework content URIs.
403+
*/
404+
private fun openForReading(path: String): InputStream {
405+
return if (path.startsWith("content://")) {
406+
reactApplicationContext.contentResolver.openInputStream(Uri.parse(path))!!
407+
} else {
408+
File(path).inputStream()
409+
}
410+
}
394411
}

0 commit comments

Comments
 (0)