@vali98/react-native-fs 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/android/generated/java/com/chatterui/reactnativelocaldownload/NativeReactNativeLocalDownloadSpec.java +12 -0
- package/android/generated/jni/RNReactNativeLocalDownloadSpec-generated.cpp +18 -0
- package/android/generated/jni/react/renderer/components/RNReactNativeLocalDownloadSpec/RNReactNativeLocalDownloadSpecJSI-generated.cpp +21 -0
- package/android/generated/jni/react/renderer/components/RNReactNativeLocalDownloadSpec/RNReactNativeLocalDownloadSpecJSI.h +27 -0
- package/android/src/main/java/com/chatterui/reactnativelocaldownload/ReactNativeLocalDownloadModule.kt +74 -0
- package/ios/ReactNativeLocalDownload.mm +29 -0
- package/ios/generated/RNReactNativeLocalDownloadSpec/RNReactNativeLocalDownloadSpec-generated.mm +21 -0
- package/ios/generated/RNReactNativeLocalDownloadSpec/RNReactNativeLocalDownloadSpec.h +9 -0
- package/ios/generated/RNReactNativeLocalDownloadSpecJSI-generated.cpp +21 -0
- package/ios/generated/RNReactNativeLocalDownloadSpecJSI.h +27 -0
- package/lib/module/NativeReactNativeLocalDownload.ts +7 -2
- package/lib/module/index.js +15 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeReactNativeLocalDownload.d.ts +3 -0
- package/lib/typescript/src/NativeReactNativeLocalDownload.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeReactNativeLocalDownload.ts +7 -2
- package/src/index.tsx +62 -45
package/README.md
CHANGED
|
@@ -35,4 +35,16 @@ public abstract class NativeReactNativeLocalDownloadSpec extends ReactContextBas
|
|
|
35
35
|
@ReactMethod
|
|
36
36
|
@DoNotStrip
|
|
37
37
|
public abstract void localDownload(String uri, Promise promise);
|
|
38
|
+
|
|
39
|
+
@ReactMethod
|
|
40
|
+
@DoNotStrip
|
|
41
|
+
public abstract void getContentFd(String uri, Promise promise);
|
|
42
|
+
|
|
43
|
+
@ReactMethod
|
|
44
|
+
@DoNotStrip
|
|
45
|
+
public abstract void closeFd(String fd, Promise promise);
|
|
46
|
+
|
|
47
|
+
@ReactMethod
|
|
48
|
+
@DoNotStrip
|
|
49
|
+
public abstract void persistContentPermission(String uri, Promise promise);
|
|
38
50
|
}
|
|
@@ -17,9 +17,27 @@ static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI
|
|
|
17
17
|
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "localDownload", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_getContentFd(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
21
|
+
static jmethodID cachedMethodId = nullptr;
|
|
22
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "getContentFd", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_closeFd(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
26
|
+
static jmethodID cachedMethodId = nullptr;
|
|
27
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "closeFd", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_persistContentPermission(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
31
|
+
static jmethodID cachedMethodId = nullptr;
|
|
32
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "persistContentPermission", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
NativeReactNativeLocalDownloadSpecJSI::NativeReactNativeLocalDownloadSpecJSI(const JavaTurboModule::InitParams ¶ms)
|
|
21
36
|
: JavaTurboModule(params) {
|
|
22
37
|
methodMap_["localDownload"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_localDownload};
|
|
38
|
+
methodMap_["getContentFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_getContentFd};
|
|
39
|
+
methodMap_["closeFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_closeFd};
|
|
40
|
+
methodMap_["persistContentPermission"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_persistContentPermission};
|
|
23
41
|
}
|
|
24
42
|
|
|
25
43
|
std::shared_ptr<TurboModule> RNReactNativeLocalDownloadSpec_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms) {
|
|
@@ -17,10 +17,31 @@ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_localD
|
|
|
17
17
|
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
|
+
static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_getContentFd(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
21
|
+
return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->getContentFd(
|
|
22
|
+
rt,
|
|
23
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_closeFd(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
27
|
+
return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->closeFd(
|
|
28
|
+
rt,
|
|
29
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_persistContentPermission(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
33
|
+
return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->persistContentPermission(
|
|
34
|
+
rt,
|
|
35
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
20
38
|
|
|
21
39
|
NativeReactNativeLocalDownloadCxxSpecJSI::NativeReactNativeLocalDownloadCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
|
|
22
40
|
: TurboModule("ReactNativeLocalDownload", jsInvoker) {
|
|
23
41
|
methodMap_["localDownload"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_localDownload};
|
|
42
|
+
methodMap_["getContentFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_getContentFd};
|
|
43
|
+
methodMap_["closeFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_closeFd};
|
|
44
|
+
methodMap_["persistContentPermission"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_persistContentPermission};
|
|
24
45
|
}
|
|
25
46
|
|
|
26
47
|
|
|
@@ -21,6 +21,9 @@ protected:
|
|
|
21
21
|
|
|
22
22
|
public:
|
|
23
23
|
virtual jsi::Value localDownload(jsi::Runtime &rt, jsi::String uri) = 0;
|
|
24
|
+
virtual jsi::Value getContentFd(jsi::Runtime &rt, jsi::String uri) = 0;
|
|
25
|
+
virtual jsi::Value closeFd(jsi::Runtime &rt, jsi::String fd) = 0;
|
|
26
|
+
virtual jsi::Value persistContentPermission(jsi::Runtime &rt, jsi::String uri) = 0;
|
|
24
27
|
|
|
25
28
|
};
|
|
26
29
|
|
|
@@ -59,6 +62,30 @@ private:
|
|
|
59
62
|
return bridging::callFromJs<jsi::Value>(
|
|
60
63
|
rt, &T::localDownload, jsInvoker_, instance_, std::move(uri));
|
|
61
64
|
}
|
|
65
|
+
jsi::Value getContentFd(jsi::Runtime &rt, jsi::String uri) override {
|
|
66
|
+
static_assert(
|
|
67
|
+
bridging::getParameterCount(&T::getContentFd) == 2,
|
|
68
|
+
"Expected getContentFd(...) to have 2 parameters");
|
|
69
|
+
|
|
70
|
+
return bridging::callFromJs<jsi::Value>(
|
|
71
|
+
rt, &T::getContentFd, jsInvoker_, instance_, std::move(uri));
|
|
72
|
+
}
|
|
73
|
+
jsi::Value closeFd(jsi::Runtime &rt, jsi::String fd) override {
|
|
74
|
+
static_assert(
|
|
75
|
+
bridging::getParameterCount(&T::closeFd) == 2,
|
|
76
|
+
"Expected closeFd(...) to have 2 parameters");
|
|
77
|
+
|
|
78
|
+
return bridging::callFromJs<jsi::Value>(
|
|
79
|
+
rt, &T::closeFd, jsInvoker_, instance_, std::move(fd));
|
|
80
|
+
}
|
|
81
|
+
jsi::Value persistContentPermission(jsi::Runtime &rt, jsi::String uri) override {
|
|
82
|
+
static_assert(
|
|
83
|
+
bridging::getParameterCount(&T::persistContentPermission) == 2,
|
|
84
|
+
"Expected persistContentPermission(...) to have 2 parameters");
|
|
85
|
+
|
|
86
|
+
return bridging::callFromJs<jsi::Value>(
|
|
87
|
+
rt, &T::persistContentPermission, jsInvoker_, instance_, std::move(uri));
|
|
88
|
+
}
|
|
62
89
|
|
|
63
90
|
private:
|
|
64
91
|
friend class NativeReactNativeLocalDownloadCxxSpec;
|
|
@@ -7,7 +7,10 @@ import com.facebook.react.module.annotations.ReactModule
|
|
|
7
7
|
import android.content.ContentValues
|
|
8
8
|
import android.content.ContentResolver
|
|
9
9
|
import android.net.Uri
|
|
10
|
+
import android.system.Os
|
|
11
|
+
import android.content.Intent
|
|
10
12
|
import android.provider.MediaStore
|
|
13
|
+
import android.os.ParcelFileDescriptor
|
|
11
14
|
import java.io.File
|
|
12
15
|
import java.io.FileInputStream
|
|
13
16
|
import java.io.FileOutputStream
|
|
@@ -21,6 +24,77 @@ class ReactNativeLocalDownloadModule(private val reactContext: ReactApplicationC
|
|
|
21
24
|
return NAME
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
override fun getContentFd(contentUri: String, promise: Promise) {
|
|
28
|
+
try {
|
|
29
|
+
val uri = Uri.parse(contentUri)
|
|
30
|
+
|
|
31
|
+
val pfd = reactContext.contentResolver
|
|
32
|
+
.openFileDescriptor(uri, "r")
|
|
33
|
+
?: run {
|
|
34
|
+
promise.reject("FD_OPEN_FAILED", "Unable to open content URI")
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
val fd = pfd.detachFd()
|
|
39
|
+
promise.resolve("/proc/self/fd/$fd")
|
|
40
|
+
|
|
41
|
+
} catch (e: Exception) {
|
|
42
|
+
promise.reject(
|
|
43
|
+
"GET_FD_ERROR",
|
|
44
|
+
"Failed to get detached FD: ${e.message}",
|
|
45
|
+
e
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
override fun closeFd(fdOrPath: String, promise: Promise) {
|
|
51
|
+
try {
|
|
52
|
+
val fdInt = when {
|
|
53
|
+
fdOrPath.startsWith("/proc/") ->
|
|
54
|
+
fdOrPath.substringAfterLast("/").toInt()
|
|
55
|
+
else ->
|
|
56
|
+
fdOrPath.toInt()
|
|
57
|
+
}
|
|
58
|
+
ParcelFileDescriptor.adoptFd(fdInt).close()
|
|
59
|
+
promise.resolve(true)
|
|
60
|
+
} catch (e: Exception) {
|
|
61
|
+
promise.reject(
|
|
62
|
+
"FD_CLOSE_ERROR",
|
|
63
|
+
"Failed to close FD: ${e.message}",
|
|
64
|
+
e
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
override fun persistContentPermission(uriString: String, promise: Promise) {
|
|
70
|
+
try {
|
|
71
|
+
val uri = Uri.parse(uriString)
|
|
72
|
+
|
|
73
|
+
val flags =
|
|
74
|
+
Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
|
75
|
+
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
76
|
+
|
|
77
|
+
reactContext.contentResolver
|
|
78
|
+
.takePersistableUriPermission(uri, flags)
|
|
79
|
+
|
|
80
|
+
promise.resolve(true)
|
|
81
|
+
|
|
82
|
+
} catch (e: SecurityException) {
|
|
83
|
+
promise.reject(
|
|
84
|
+
"PERSIST_PERMISSION_DENIED",
|
|
85
|
+
"Persistable permission not granted for this URI",
|
|
86
|
+
e
|
|
87
|
+
)
|
|
88
|
+
} catch (e: Exception) {
|
|
89
|
+
promise.reject(
|
|
90
|
+
"PERSIST_PERMISSION_ERROR",
|
|
91
|
+
e.message,
|
|
92
|
+
e
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
24
98
|
override fun localDownload(uri: String, promise: Promise) {
|
|
25
99
|
try {
|
|
26
100
|
val inputFile = File(uri)
|
|
@@ -26,10 +26,39 @@ RCT_EXPORT_MODULE()
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
#pragma mark - Android-only FD stubs (iOS)
|
|
30
|
+
|
|
31
|
+
- (void)getContentFd:(NSString *)uri
|
|
32
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
33
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
34
|
+
{
|
|
35
|
+
reject(
|
|
36
|
+
@"UNSUPPORTED_PLATFORM",
|
|
37
|
+
@"getContentFd is not supported on iOS",
|
|
38
|
+
nil
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
- (void)closeFd:(NSString *)fdOrPath
|
|
43
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
44
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
45
|
+
{
|
|
46
|
+
// No-op stub for iOS
|
|
47
|
+
resolve(@(YES));
|
|
48
|
+
}
|
|
49
|
+
|
|
29
50
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
30
51
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
31
52
|
{
|
|
32
53
|
return std::make_shared<facebook::react::NativeReactNativeLocalDownloadSpecJSI>(params);
|
|
33
54
|
}
|
|
34
55
|
|
|
56
|
+
- (void)persistContentPermission:(NSString *)uri
|
|
57
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
58
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
59
|
+
{
|
|
60
|
+
// iOS sandbox does not support persistable URI permissions
|
|
61
|
+
resolve(@(YES));
|
|
62
|
+
}
|
|
63
|
+
|
|
35
64
|
@end
|
package/ios/generated/RNReactNativeLocalDownloadSpec/RNReactNativeLocalDownloadSpec-generated.mm
CHANGED
|
@@ -30,10 +30,31 @@ namespace facebook::react {
|
|
|
30
30
|
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "localDownload", @selector(localDownload:resolve:reject:), args, count);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_getContentFd(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
34
|
+
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "getContentFd", @selector(getContentFd:resolve:reject:), args, count);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_closeFd(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
38
|
+
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "closeFd", @selector(closeFd:resolve:reject:), args, count);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static facebook::jsi::Value __hostFunction_NativeReactNativeLocalDownloadSpecJSI_persistContentPermission(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
42
|
+
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "persistContentPermission", @selector(persistContentPermission:resolve:reject:), args, count);
|
|
43
|
+
}
|
|
44
|
+
|
|
33
45
|
NativeReactNativeLocalDownloadSpecJSI::NativeReactNativeLocalDownloadSpecJSI(const ObjCTurboModule::InitParams ¶ms)
|
|
34
46
|
: ObjCTurboModule(params) {
|
|
35
47
|
|
|
36
48
|
methodMap_["localDownload"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_localDownload};
|
|
37
49
|
|
|
50
|
+
|
|
51
|
+
methodMap_["getContentFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_getContentFd};
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
methodMap_["closeFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_closeFd};
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
methodMap_["persistContentPermission"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadSpecJSI_persistContentPermission};
|
|
58
|
+
|
|
38
59
|
}
|
|
39
60
|
} // namespace facebook::react
|
|
@@ -38,6 +38,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
38
38
|
- (void)localDownload:(NSString *)uri
|
|
39
39
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
40
40
|
reject:(RCTPromiseRejectBlock)reject;
|
|
41
|
+
- (void)getContentFd:(NSString *)uri
|
|
42
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
43
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
44
|
+
- (void)closeFd:(NSString *)fd
|
|
45
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
46
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
47
|
+
- (void)persistContentPermission:(NSString *)uri
|
|
48
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
49
|
+
reject:(RCTPromiseRejectBlock)reject;
|
|
41
50
|
|
|
42
51
|
@end
|
|
43
52
|
|
|
@@ -17,10 +17,31 @@ static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_localD
|
|
|
17
17
|
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
|
+
static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_getContentFd(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
21
|
+
return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->getContentFd(
|
|
22
|
+
rt,
|
|
23
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_closeFd(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
27
|
+
return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->closeFd(
|
|
28
|
+
rt,
|
|
29
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
static jsi::Value __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_persistContentPermission(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
|
33
|
+
return static_cast<NativeReactNativeLocalDownloadCxxSpecJSI *>(&turboModule)->persistContentPermission(
|
|
34
|
+
rt,
|
|
35
|
+
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
20
38
|
|
|
21
39
|
NativeReactNativeLocalDownloadCxxSpecJSI::NativeReactNativeLocalDownloadCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
|
|
22
40
|
: TurboModule("ReactNativeLocalDownload", jsInvoker) {
|
|
23
41
|
methodMap_["localDownload"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_localDownload};
|
|
42
|
+
methodMap_["getContentFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_getContentFd};
|
|
43
|
+
methodMap_["closeFd"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_closeFd};
|
|
44
|
+
methodMap_["persistContentPermission"] = MethodMetadata {1, __hostFunction_NativeReactNativeLocalDownloadCxxSpecJSI_persistContentPermission};
|
|
24
45
|
}
|
|
25
46
|
|
|
26
47
|
|
|
@@ -21,6 +21,9 @@ protected:
|
|
|
21
21
|
|
|
22
22
|
public:
|
|
23
23
|
virtual jsi::Value localDownload(jsi::Runtime &rt, jsi::String uri) = 0;
|
|
24
|
+
virtual jsi::Value getContentFd(jsi::Runtime &rt, jsi::String uri) = 0;
|
|
25
|
+
virtual jsi::Value closeFd(jsi::Runtime &rt, jsi::String fd) = 0;
|
|
26
|
+
virtual jsi::Value persistContentPermission(jsi::Runtime &rt, jsi::String uri) = 0;
|
|
24
27
|
|
|
25
28
|
};
|
|
26
29
|
|
|
@@ -59,6 +62,30 @@ private:
|
|
|
59
62
|
return bridging::callFromJs<jsi::Value>(
|
|
60
63
|
rt, &T::localDownload, jsInvoker_, instance_, std::move(uri));
|
|
61
64
|
}
|
|
65
|
+
jsi::Value getContentFd(jsi::Runtime &rt, jsi::String uri) override {
|
|
66
|
+
static_assert(
|
|
67
|
+
bridging::getParameterCount(&T::getContentFd) == 2,
|
|
68
|
+
"Expected getContentFd(...) to have 2 parameters");
|
|
69
|
+
|
|
70
|
+
return bridging::callFromJs<jsi::Value>(
|
|
71
|
+
rt, &T::getContentFd, jsInvoker_, instance_, std::move(uri));
|
|
72
|
+
}
|
|
73
|
+
jsi::Value closeFd(jsi::Runtime &rt, jsi::String fd) override {
|
|
74
|
+
static_assert(
|
|
75
|
+
bridging::getParameterCount(&T::closeFd) == 2,
|
|
76
|
+
"Expected closeFd(...) to have 2 parameters");
|
|
77
|
+
|
|
78
|
+
return bridging::callFromJs<jsi::Value>(
|
|
79
|
+
rt, &T::closeFd, jsInvoker_, instance_, std::move(fd));
|
|
80
|
+
}
|
|
81
|
+
jsi::Value persistContentPermission(jsi::Runtime &rt, jsi::String uri) override {
|
|
82
|
+
static_assert(
|
|
83
|
+
bridging::getParameterCount(&T::persistContentPermission) == 2,
|
|
84
|
+
"Expected persistContentPermission(...) to have 2 parameters");
|
|
85
|
+
|
|
86
|
+
return bridging::callFromJs<jsi::Value>(
|
|
87
|
+
rt, &T::persistContentPermission, jsInvoker_, instance_, std::move(uri));
|
|
88
|
+
}
|
|
62
89
|
|
|
63
90
|
private:
|
|
64
91
|
friend class NativeReactNativeLocalDownloadCxxSpec;
|
|
@@ -2,7 +2,12 @@ import type { TurboModule } from 'react-native';
|
|
|
2
2
|
import { TurboModuleRegistry } from 'react-native';
|
|
3
3
|
|
|
4
4
|
export interface Spec extends TurboModule {
|
|
5
|
-
localDownload(uri: string): Promise<void
|
|
5
|
+
localDownload(uri: string): Promise<void>;
|
|
6
|
+
getContentFd(uri: string): Promise<string>;
|
|
7
|
+
closeFd(fd: string): Promise<void>;
|
|
8
|
+
persistContentPermission(uri: string): Promise<void>;
|
|
6
9
|
}
|
|
7
10
|
|
|
8
|
-
export default TurboModuleRegistry.getEnforcing<Spec>(
|
|
11
|
+
export default TurboModuleRegistry.getEnforcing<Spec>(
|
|
12
|
+
'ReactNativeLocalDownload'
|
|
13
|
+
);
|
package/lib/module/index.js
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
import ReactNativeLocalDownload from './NativeReactNativeLocalDownload';
|
|
4
4
|
import { PermissionsAndroid, Platform, Linking, Alert } from 'react-native';
|
|
5
|
+
export async function getContentFd(uri) {
|
|
6
|
+
if (Platform.OS !== 'android') return;
|
|
7
|
+
if (!uri.includes('content://')) {
|
|
8
|
+
throw new TypeError('URI provided is not a content URI');
|
|
9
|
+
}
|
|
10
|
+
return ReactNativeLocalDownload.getContentFd(uri);
|
|
11
|
+
}
|
|
12
|
+
export async function closeFd(uri) {
|
|
13
|
+
if (Platform.OS !== 'android') return;
|
|
14
|
+
return ReactNativeLocalDownload.closeFd(uri);
|
|
15
|
+
}
|
|
16
|
+
export async function persistContentPermission(uri) {
|
|
17
|
+
if (Platform.OS !== 'android') return;
|
|
18
|
+
return ReactNativeLocalDownload.persistContentPermission(uri);
|
|
19
|
+
}
|
|
5
20
|
/**
|
|
6
21
|
*
|
|
7
22
|
* @param uri path to content to be sent to downloads
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["ReactNativeLocalDownload","PermissionsAndroid","Platform","Linking","Alert","
|
|
1
|
+
{"version":3,"names":["ReactNativeLocalDownload","PermissionsAndroid","Platform","Linking","Alert","getContentFd","uri","OS","includes","TypeError","closeFd","persistContentPermission","localDownload","never_ask_function","granted","requestStoragePermission","RESULTS","NEVER_ASK_AGAIN","alert","text","style","onPress","openSettings","Error","GRANTED","Number","Version","hasPermission","check","PERMISSIONS","WRITE_EXTERNAL_STORAGE","request","title","message","buttonPositive","err","console","warn","DENIED"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,wBAAwB,MAAM,kCAAkC;AAEvE,SAASC,kBAAkB,EAAEC,QAAQ,EAAEC,OAAO,EAAEC,KAAK,QAAQ,cAAc;AAE3E,OAAO,eAAeC,YAAYA,CAACC,GAAW,EAAE;EAC9C,IAAIJ,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE;EAC/B,IAAI,CAACD,GAAG,CAACE,QAAQ,CAAC,YAAY,CAAC,EAAE;IAC/B,MAAM,IAAIC,SAAS,CAAC,mCAAmC,CAAC;EAC1D;EACA,OAAOT,wBAAwB,CAACK,YAAY,CAACC,GAAG,CAAC;AACnD;AAEA,OAAO,eAAeI,OAAOA,CAACJ,GAAW,EAAE;EACzC,IAAIJ,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE;EAC/B,OAAOP,wBAAwB,CAACU,OAAO,CAACJ,GAAG,CAAC;AAC9C;AAEA,OAAO,eAAeK,wBAAwBA,CAACL,GAAW,EAAE;EAC1D,IAAIJ,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE;EAC/B,OAAOP,wBAAwB,CAACW,wBAAwB,CAACL,GAAG,CAAC;AAC/D;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeM,aAAaA,CACjCN,GAAW,EACXO,kBAA2C,EAC5B;EACf,MAAMC,OAAO,GAAG,MAAMC,wBAAwB,CAAC,CAAC;EAChD,IAAID,OAAO,KAAK,SAAS,EAAE;IACzB,IAAIA,OAAO,KAAKb,kBAAkB,CAACe,OAAO,CAACC,eAAe,EAAE;MAC1D,IAAI,CAACJ,kBAAkB,EACrBT,KAAK,CAACc,KAAK,CACT,qBAAqB,EACrB,6EAA6E,EAC7E,CACE;QAAEC,IAAI,EAAE,QAAQ;QAAEC,KAAK,EAAE;MAAS,CAAC,EACnC;QAAED,IAAI,EAAE,eAAe;QAAEE,OAAO,EAAEA,CAAA,KAAMlB,OAAO,CAACmB,YAAY,CAAC;MAAE,CAAC,CAEpE,CAAC,CAAC,KACCT,kBAAkB,CAAC,CAAC;IAC3B;IACA,MAAM,IAAIU,KAAK,CAAC,yBAAyB,CAAC;EAC5C;EACA,OAAOvB,wBAAwB,CAACY,aAAa,CAACN,GAAG,CAAC;AACpD;AAEA,OAAO,MAAMS,wBAAwB,GAAG,MAAAA,CAAA,KAEnC;EACH,IAAIb,QAAQ,CAACK,EAAE,KAAK,SAAS,EAAE,OAAON,kBAAkB,CAACe,OAAO,CAACQ,OAAO;EAExE,IAAI;IACF,IAAIC,MAAM,CAACvB,QAAQ,CAACwB,OAAO,CAAC,IAAI,EAAE,EAAE;MAClC,OAAO,SAAS;IAClB;IAEA,MAAMC,aAAa,GAAG,MAAM1B,kBAAkB,CAAC2B,KAAK,CAClD3B,kBAAkB,CAAC4B,WAAW,CAACC,sBACjC,CAAC;IACD,IAAIH,aAAa,EAAE;MACjB,OAAO,SAAS;IAClB;IAEA,MAAMb,OAAO,GAAG,MAAMb,kBAAkB,CAAC8B,OAAO,CAC9C9B,kBAAkB,CAAC4B,WAAW,CAACC,sBAAsB,EACrD;MACEE,KAAK,EAAE,oBAAoB;MAC3BC,OAAO,EAAE,0DAA0D;MACnEC,cAAc,EAAE;IAClB,CACF,CAAC;IACD,OAAOpB,OAAO;EAChB,CAAC,CAAC,OAAOqB,GAAG,EAAE;IACZC,OAAO,CAACC,IAAI,CAACF,GAAG,CAAC;IACjB,OAAOlC,kBAAkB,CAACe,OAAO,CAACsB,MAAM;EAC1C;AACF,CAAC","ignoreList":[]}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { TurboModule } from 'react-native';
|
|
2
2
|
export interface Spec extends TurboModule {
|
|
3
3
|
localDownload(uri: string): Promise<void>;
|
|
4
|
+
getContentFd(uri: string): Promise<string>;
|
|
5
|
+
closeFd(fd: string): Promise<void>;
|
|
6
|
+
persistContentPermission(uri: string): Promise<void>;
|
|
4
7
|
}
|
|
5
8
|
declare const _default: Spec;
|
|
6
9
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeReactNativeLocalDownload.d.ts","sourceRoot":"","sources":["../../../src/NativeReactNativeLocalDownload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"NativeReactNativeLocalDownload.d.ts","sourceRoot":"","sources":["../../../src/NativeReactNativeLocalDownload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;;AAED,wBAEE"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { PermissionsAndroid } from 'react-native';
|
|
2
|
+
export declare function getContentFd(uri: string): Promise<string | undefined>;
|
|
3
|
+
export declare function closeFd(uri: string): Promise<void>;
|
|
4
|
+
export declare function persistContentPermission(uri: string): Promise<void>;
|
|
2
5
|
/**
|
|
3
6
|
*
|
|
4
7
|
* @param uri path to content to be sent to downloads
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAA4B,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAA4B,MAAM,cAAc,CAAC;AAE5E,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,+BAM7C;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,iBAGxC;AAED,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,iBAGzD;AACD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,kBAAkB,CAAC,EAAE,MAAM,IAAI,GAAG,SAAS,GAC1C,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED,eAAO,MAAM,wBAAwB,QAAa,OAAO,CACvD,OAAO,kBAAkB,CAAC,OAAO,CAAC,MAAM,CA6BzC,CAAC"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,12 @@ import type { TurboModule } from 'react-native';
|
|
|
2
2
|
import { TurboModuleRegistry } from 'react-native';
|
|
3
3
|
|
|
4
4
|
export interface Spec extends TurboModule {
|
|
5
|
-
localDownload(uri: string): Promise<void
|
|
5
|
+
localDownload(uri: string): Promise<void>;
|
|
6
|
+
getContentFd(uri: string): Promise<string>;
|
|
7
|
+
closeFd(fd: string): Promise<void>;
|
|
8
|
+
persistContentPermission(uri: string): Promise<void>;
|
|
6
9
|
}
|
|
7
10
|
|
|
8
|
-
export default TurboModuleRegistry.getEnforcing<Spec>(
|
|
11
|
+
export default TurboModuleRegistry.getEnforcing<Spec>(
|
|
12
|
+
'ReactNativeLocalDownload'
|
|
13
|
+
);
|
package/src/index.tsx
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import ReactNativeLocalDownload from './NativeReactNativeLocalDownload';
|
|
2
2
|
|
|
3
|
-
import { PermissionsAndroid, Platform, Linking, Alert } from 'react-native'
|
|
3
|
+
import { PermissionsAndroid, Platform, Linking, Alert } from 'react-native';
|
|
4
|
+
|
|
5
|
+
export async function getContentFd(uri: string) {
|
|
6
|
+
if (Platform.OS !== 'android') return;
|
|
7
|
+
if (!uri.includes('content://')) {
|
|
8
|
+
throw new TypeError('URI provided is not a content URI');
|
|
9
|
+
}
|
|
10
|
+
return ReactNativeLocalDownload.getContentFd(uri);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function closeFd(uri: string) {
|
|
14
|
+
if (Platform.OS !== 'android') return;
|
|
15
|
+
return ReactNativeLocalDownload.closeFd(uri);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function persistContentPermission(uri: string) {
|
|
19
|
+
if (Platform.OS !== 'android') return;
|
|
20
|
+
return ReactNativeLocalDownload.persistContentPermission(uri);
|
|
21
|
+
}
|
|
4
22
|
/**
|
|
5
23
|
*
|
|
6
24
|
* @param uri path to content to be sent to downloads
|
|
@@ -8,57 +26,56 @@ import { PermissionsAndroid, Platform, Linking, Alert } from 'react-native'
|
|
|
8
26
|
* @returns
|
|
9
27
|
*/
|
|
10
28
|
export async function localDownload(
|
|
11
|
-
|
|
12
|
-
|
|
29
|
+
uri: string,
|
|
30
|
+
never_ask_function?: () => void | undefined
|
|
13
31
|
): Promise<void> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
throw new Error('Permissions not granted')
|
|
32
|
+
const granted = await requestStoragePermission();
|
|
33
|
+
if (granted !== 'granted') {
|
|
34
|
+
if (granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
|
|
35
|
+
if (!never_ask_function)
|
|
36
|
+
Alert.alert(
|
|
37
|
+
'Permission Required',
|
|
38
|
+
'You have permanently denied storage access. Please enable it from settings.',
|
|
39
|
+
[
|
|
40
|
+
{ text: 'Cancel', style: 'cancel' },
|
|
41
|
+
{ text: 'Open Settings', onPress: () => Linking.openSettings() },
|
|
42
|
+
]
|
|
43
|
+
);
|
|
44
|
+
else never_ask_function();
|
|
29
45
|
}
|
|
30
|
-
|
|
46
|
+
throw new Error('Permissions not granted');
|
|
47
|
+
}
|
|
48
|
+
return ReactNativeLocalDownload.localDownload(uri);
|
|
31
49
|
}
|
|
32
50
|
|
|
33
51
|
export const requestStoragePermission = async (): Promise<
|
|
34
|
-
|
|
52
|
+
typeof PermissionsAndroid.RESULTS.DENIED
|
|
35
53
|
> => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
if (Number(Platform.Version) >= 33) {
|
|
40
|
-
return 'granted'
|
|
41
|
-
}
|
|
54
|
+
if (Platform.OS !== 'android') return PermissionsAndroid.RESULTS.GRANTED;
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return 'granted'
|
|
48
|
-
}
|
|
56
|
+
try {
|
|
57
|
+
if (Number(Platform.Version) >= 33) {
|
|
58
|
+
return 'granted';
|
|
59
|
+
}
|
|
49
60
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
buttonPositive: 'OK',
|
|
56
|
-
}
|
|
57
|
-
)
|
|
58
|
-
return granted
|
|
59
|
-
} catch (err) {
|
|
60
|
-
console.warn(err)
|
|
61
|
-
return PermissionsAndroid.RESULTS.DENIED
|
|
61
|
+
const hasPermission = await PermissionsAndroid.check(
|
|
62
|
+
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
|
|
63
|
+
);
|
|
64
|
+
if (hasPermission) {
|
|
65
|
+
return 'granted';
|
|
62
66
|
}
|
|
63
|
-
}
|
|
64
67
|
|
|
68
|
+
const granted = await PermissionsAndroid.request(
|
|
69
|
+
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
|
70
|
+
{
|
|
71
|
+
title: 'Storage Permission',
|
|
72
|
+
message: 'This app needs access to your storage to download files.',
|
|
73
|
+
buttonPositive: 'OK',
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
return granted;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.warn(err);
|
|
79
|
+
return PermissionsAndroid.RESULTS.DENIED;
|
|
80
|
+
}
|
|
81
|
+
};
|