@react-native-documents/picker 11.0.4 → 12.0.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/android/src/main/java/com/reactnativedocumentpicker/CopyDestination.kt +1 -2
- package/android/src/main/java/com/reactnativedocumentpicker/DocumentMetadataBuilder.kt +4 -3
- package/android/src/main/java/com/reactnativedocumentpicker/FileOperations.kt +2 -2
- package/android/src/main/java/com/reactnativedocumentpicker/IsKnownTypeImpl.kt +26 -28
- package/android/src/main/java/com/reactnativedocumentpicker/MetadataGetter.kt +69 -45
- package/android/src/main/java/com/reactnativedocumentpicker/PickOptions.kt +43 -23
- package/android/src/main/java/com/reactnativedocumentpicker/PromiseWrapper.kt +83 -0
- package/android/src/main/java/com/reactnativedocumentpicker/RNDocumentPickerModule.kt +5 -5
- package/ios/RNDocumentPicker.mm +19 -41
- package/ios/swift/DocPicker.swift +46 -28
- package/ios/swift/DocSaver.swift +18 -23
- package/ios/swift/DocumentMetadataBuilder.swift +1 -1
- package/ios/swift/FileOperations.swift +21 -19
- package/ios/swift/IsKnownTypeImpl.swift +16 -10
- package/ios/swift/LocalCopyResponse.swift +3 -5
- package/ios/swift/PickerBase.swift +31 -49
- package/ios/swift/PickerOptions.swift +53 -23
- package/ios/swift/PromiseWrapper.swift +71 -58
- package/ios/swift/SaverOptions.swift +39 -17
- package/jest/build/src/errors.js +2 -0
- package/lib/module/errors.js +3 -2
- package/lib/module/errors.js.map +1 -1
- package/lib/module/release.js +1 -1
- package/lib/typescript/src/errors.d.ts +6 -3
- package/lib/typescript/src/errors.d.ts.map +1 -1
- package/lib/typescript/src/release.d.ts +1 -1
- package/lib/typescript/src/saveDocuments.d.ts +1 -1
- package/lib/typescript/src/types.d.ts +3 -3
- package/package.json +3 -2
- package/react-native-document-picker.podspec +1 -0
- package/src/errors.ts +8 -4
- package/src/release.ts +1 -1
- package/src/saveDocuments.ts +1 -1
- package/src/types.ts +3 -3
- package/android/src/main/java/com/reactnativedocumentpicker/IntentFactory.kt +0 -36
- package/android/src/main/java/com/reactnativedocumentpicker/PromiseWrapper.java +0 -105
- package/ios/swift/PromiseSupport.swift +0 -2
|
@@ -6,7 +6,6 @@ enum class CopyDestination(val preset: String) {
|
|
|
6
6
|
DOCUMENT_DIRECTORY("documentDirectory");
|
|
7
7
|
|
|
8
8
|
companion object {
|
|
9
|
-
|
|
10
|
-
fun fromPath(path: String): CopyDestination = values().find { it.preset == path } ?: CACHES_DIRECTORY
|
|
9
|
+
fun fromPath(path: String): CopyDestination = entries.find { it.preset == path } ?: CACHES_DIRECTORY
|
|
11
10
|
}
|
|
12
11
|
}
|
|
@@ -52,10 +52,11 @@ class DocumentMetadataBuilder(forUri: Uri) {
|
|
|
52
52
|
openableMimeTypes?.let {
|
|
53
53
|
val arrayOfExtensionsAndMime = Arguments.createArray()
|
|
54
54
|
it.forEach { mimeType ->
|
|
55
|
-
val virtualFileDetails = Arguments.createMap()
|
|
56
55
|
val maybeExtension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
|
|
57
|
-
virtualFileDetails.
|
|
58
|
-
|
|
56
|
+
val virtualFileDetails = Arguments.createMap().apply {
|
|
57
|
+
putString("mimeType", mimeType)
|
|
58
|
+
putString("extension", maybeExtension)
|
|
59
|
+
}
|
|
59
60
|
arrayOfExtensionsAndMime.pushMap(virtualFileDetails)
|
|
60
61
|
}
|
|
61
62
|
map.putArray("convertibleToMimeTypes", arrayOfExtensionsAndMime)
|
|
@@ -189,8 +189,8 @@ class FileOperations(private val uriMap: MutableMap<String, Uri>) {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
val copyStreamToAnother: (InputStream, OutputStream) -> Long = { inputStream, outputStream ->
|
|
192
|
-
inputStream.use {
|
|
193
|
-
outputStream.use {
|
|
192
|
+
inputStream.use { _ ->
|
|
193
|
+
outputStream.use { _ ->
|
|
194
194
|
val bytesCopied = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
195
195
|
FileUtils.copy(inputStream, outputStream)
|
|
196
196
|
} else {
|
|
@@ -4,37 +4,35 @@ import android.webkit.MimeTypeMap
|
|
|
4
4
|
import com.facebook.react.bridge.Arguments
|
|
5
5
|
import com.facebook.react.bridge.WritableMap
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
"extension" -> {
|
|
20
|
-
val mimeForExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(value)
|
|
21
|
-
createMap(
|
|
22
|
-
isKnown = mimeForExtension != null,
|
|
23
|
-
preferredFilenameExtension = if (mimeForExtension != null) value else null,
|
|
24
|
-
mimeType = mimeForExtension
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
else -> createMap(isKnown = false, preferredFilenameExtension = null, mimeType = null)
|
|
7
|
+
object IsKnownTypeImpl {
|
|
8
|
+
fun isKnownType(kind: String, value: String): WritableMap {
|
|
9
|
+
return when (kind) {
|
|
10
|
+
"mimeType" -> {
|
|
11
|
+
val extensionForMime = MimeTypeMap.getSingleton().getExtensionFromMimeType(value)
|
|
12
|
+
createMap(
|
|
13
|
+
isKnown = extensionForMime != null,
|
|
14
|
+
preferredFilenameExtension = extensionForMime,
|
|
15
|
+
mimeType = if (extensionForMime != null) value else null
|
|
16
|
+
)
|
|
28
17
|
}
|
|
18
|
+
"extension" -> {
|
|
19
|
+
val mimeForExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(value)
|
|
20
|
+
createMap(
|
|
21
|
+
isKnown = mimeForExtension != null,
|
|
22
|
+
preferredFilenameExtension = if (mimeForExtension != null) value else null,
|
|
23
|
+
mimeType = mimeForExtension
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
else -> createMap(isKnown = false, preferredFilenameExtension = null, mimeType = null)
|
|
29
27
|
}
|
|
28
|
+
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
30
|
+
private fun createMap(isKnown: Boolean, preferredFilenameExtension: String?, mimeType: String?): WritableMap {
|
|
31
|
+
return Arguments.createMap().apply {
|
|
32
|
+
putNull("UTType")
|
|
33
|
+
putBoolean("isKnown", isKnown)
|
|
34
|
+
putString("preferredFilenameExtension", preferredFilenameExtension)
|
|
35
|
+
putString("mimeType", mimeType)
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
}
|
|
@@ -75,17 +75,39 @@ class MetadataGetter(private val uriMap: MutableMap<String, Uri>) {
|
|
|
75
75
|
contentResolver: ContentResolver,
|
|
76
76
|
metadataBuilder: DocumentMetadataBuilder,
|
|
77
77
|
couldBeVirtualFile: Boolean
|
|
78
|
+
) {
|
|
79
|
+
try {
|
|
80
|
+
queryContentResolverMetadataInternal(contentResolver, metadataBuilder, couldBeVirtualFile)
|
|
81
|
+
} catch (e: Exception) {
|
|
82
|
+
val suppressedSummary =
|
|
83
|
+
e.suppressed.joinToString(separator = "; ") { suppressed ->
|
|
84
|
+
"${suppressed.javaClass.simpleName}: ${suppressed.message ?: "no message"}"
|
|
85
|
+
}
|
|
86
|
+
metadataBuilder.metadataReadingError(
|
|
87
|
+
"Could not read file metadata: ${e.javaClass.simpleName}: ${e.message ?: "no message"}" +
|
|
88
|
+
(" (suppressed summary: [$suppressedSummary])")
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private fun queryContentResolverMetadataInternal(
|
|
94
|
+
contentResolver: ContentResolver,
|
|
95
|
+
metadataBuilder: DocumentMetadataBuilder,
|
|
96
|
+
couldBeVirtualFile: Boolean
|
|
78
97
|
) {
|
|
79
98
|
val forUri = metadataBuilder.getUri()
|
|
99
|
+
val hasNoMime = !metadataBuilder.hasMime()
|
|
80
100
|
|
|
81
101
|
val projection = mutableListOf(
|
|
82
|
-
DocumentsContract.Document.COLUMN_MIME_TYPE,
|
|
83
102
|
OpenableColumns.DISPLAY_NAME,
|
|
84
103
|
OpenableColumns.SIZE,
|
|
85
104
|
).apply {
|
|
86
105
|
if (couldBeVirtualFile) {
|
|
87
106
|
add(DocumentsContract.Document.COLUMN_FLAGS)
|
|
88
107
|
}
|
|
108
|
+
if (hasNoMime) {
|
|
109
|
+
add(DocumentsContract.Document.COLUMN_MIME_TYPE)
|
|
110
|
+
}
|
|
89
111
|
}.toTypedArray()
|
|
90
112
|
|
|
91
113
|
contentResolver
|
|
@@ -97,39 +119,41 @@ class MetadataGetter(private val uriMap: MutableMap<String, Uri>) {
|
|
|
97
119
|
null
|
|
98
120
|
)
|
|
99
121
|
.use { cursor ->
|
|
100
|
-
if (cursor
|
|
101
|
-
metadataBuilder.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
122
|
+
if (cursor == null) {
|
|
123
|
+
metadataBuilder.metadataReadingError("Could not read file metadata because cursor was null. This is likely an issue with the underlying ContentProvider.")
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
if (!cursor.moveToFirst()) {
|
|
127
|
+
metadataBuilder.metadataReadingError("Could not read file metadata because cursor could not move to the first result row. This is likely an issue with the underlying ContentProvider. Row count: ${cursor.count}, columns: ${cursor.columnNames.joinToString(",")}")
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
metadataBuilder.name(
|
|
131
|
+
getCursorValue(cursor, OpenableColumns.DISPLAY_NAME, String::class.java)
|
|
132
|
+
)
|
|
133
|
+
metadataBuilder.size(getCursorValue(cursor, OpenableColumns.SIZE, Long::class.java))
|
|
134
|
+
|
|
135
|
+
if (hasNoMime) {
|
|
136
|
+
metadataBuilder.mimeType(
|
|
137
|
+
getCursorValue(
|
|
138
|
+
cursor, DocumentsContract.Document.COLUMN_MIME_TYPE, String::class.java
|
|
110
139
|
)
|
|
111
|
-
|
|
140
|
+
)
|
|
141
|
+
}
|
|
112
142
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
metadataBuilder.size(getCursorValue(cursor, OpenableColumns.SIZE, Long::class.java))
|
|
129
|
-
} else {
|
|
130
|
-
// metadataBuilder only contains the uri, type and error in this unlikely case
|
|
131
|
-
// there's nothing more we can do
|
|
132
|
-
metadataBuilder.metadataReadingError("Could not read file metadata")
|
|
143
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
144
|
+
// https://developer.android.com/training/data-storage/shared/documents-files#open-virtual-file
|
|
145
|
+
val isVirtual =
|
|
146
|
+
if (couldBeVirtualFile) {
|
|
147
|
+
val cursorValue: Int =
|
|
148
|
+
getCursorValue(
|
|
149
|
+
cursor, DocumentsContract.Document.COLUMN_FLAGS, Int::class.java
|
|
150
|
+
)
|
|
151
|
+
?: 0
|
|
152
|
+
cursorValue and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT != 0
|
|
153
|
+
} else {
|
|
154
|
+
false
|
|
155
|
+
}
|
|
156
|
+
metadataBuilder.virtual(isVirtual)
|
|
133
157
|
}
|
|
134
158
|
}
|
|
135
159
|
}
|
|
@@ -137,19 +161,19 @@ class MetadataGetter(private val uriMap: MutableMap<String, Uri>) {
|
|
|
137
161
|
@Suppress("UNCHECKED_CAST")
|
|
138
162
|
private fun <T> getCursorValue(cursor: Cursor, columnName: String, valueType: Class<T>): T? {
|
|
139
163
|
val columnIndex = cursor.getColumnIndex(columnName)
|
|
140
|
-
if (columnIndex
|
|
141
|
-
return
|
|
142
|
-
when (valueType) {
|
|
143
|
-
String::class.java -> cursor.getString(columnIndex) as T
|
|
144
|
-
Int::class.java -> cursor.getInt(columnIndex) as T
|
|
145
|
-
Long::class.java -> cursor.getLong(columnIndex) as T
|
|
146
|
-
Double::class.java -> cursor.getDouble(columnIndex) as T
|
|
147
|
-
Float::class.java -> cursor.getFloat(columnIndex) as T
|
|
148
|
-
else -> null
|
|
149
|
-
}
|
|
150
|
-
// throw should not happen but if it does, we return null
|
|
151
|
-
}.getOrNull()
|
|
164
|
+
if (columnIndex == -1 || cursor.isNull(columnIndex)) {
|
|
165
|
+
return null
|
|
152
166
|
}
|
|
153
|
-
return
|
|
167
|
+
return runCatching {
|
|
168
|
+
when (valueType) {
|
|
169
|
+
String::class.java -> cursor.getString(columnIndex) as T
|
|
170
|
+
Int::class.java -> cursor.getInt(columnIndex) as T
|
|
171
|
+
Long::class.java -> cursor.getLong(columnIndex) as T
|
|
172
|
+
Double::class.java -> cursor.getDouble(columnIndex) as T
|
|
173
|
+
Float::class.java -> cursor.getFloat(columnIndex) as T
|
|
174
|
+
else -> null
|
|
175
|
+
}
|
|
176
|
+
// throw should not happen but if it does, we return null
|
|
177
|
+
}.getOrNull()
|
|
154
178
|
}
|
|
155
179
|
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
package com.reactnativedocumentpicker
|
|
3
3
|
|
|
4
4
|
import android.content.Intent
|
|
5
|
+
import android.os.Build
|
|
6
|
+
import android.provider.DocumentsContract
|
|
5
7
|
import com.facebook.react.bridge.ReadableArray
|
|
6
8
|
import com.facebook.react.bridge.ReadableMap
|
|
7
9
|
|
|
@@ -14,6 +16,20 @@ data class PickOptions(
|
|
|
14
16
|
val requestLongTermAccess: Boolean,
|
|
15
17
|
val allowVirtualFiles: Boolean,
|
|
16
18
|
) {
|
|
19
|
+
constructor(readableMap: ReadableMap) : this(
|
|
20
|
+
mode = readableMap.getString("mode"),
|
|
21
|
+
mimeTypes = if (readableMap.hasKey("type") && !readableMap.isNull("type")) {
|
|
22
|
+
readableMap.getArray("type")?.let { readableArrayToStringArray(it) } ?: arrayOf("*/*")
|
|
23
|
+
} else {
|
|
24
|
+
arrayOf("*/*")
|
|
25
|
+
},
|
|
26
|
+
initialDirectoryUrl = if (readableMap.hasKey("initialDirectoryUrl")) readableMap.getString("initialDirectoryUrl") else null,
|
|
27
|
+
localOnly = readableMap.hasKey("localOnly") && readableMap.getBoolean("localOnly"),
|
|
28
|
+
multiple = readableMap.hasKey("allowMultiSelection") && readableMap.getBoolean("allowMultiSelection"),
|
|
29
|
+
requestLongTermAccess = readableMap.hasKey("requestLongTermAccess") && readableMap.getBoolean("requestLongTermAccess"),
|
|
30
|
+
allowVirtualFiles = readableMap.hasKey("allowVirtualFiles") && readableMap.getBoolean("allowVirtualFiles")
|
|
31
|
+
)
|
|
32
|
+
|
|
17
33
|
val action: String
|
|
18
34
|
get() = if ("open" == mode) Intent.ACTION_OPEN_DOCUMENT else Intent.ACTION_GET_CONTENT
|
|
19
35
|
|
|
@@ -26,33 +42,37 @@ data class PickOptions(
|
|
|
26
42
|
mimeTypes.joinToString("|")
|
|
27
43
|
}
|
|
28
44
|
}
|
|
29
|
-
}
|
|
30
45
|
|
|
31
|
-
fun
|
|
32
|
-
|
|
46
|
+
fun getPickIntent(): Intent {
|
|
47
|
+
// TODO option for extra task on stack?
|
|
48
|
+
// reminder - flags are for granting rights to others
|
|
33
49
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} else {
|
|
37
|
-
arrayOf("*/*")
|
|
38
|
-
}
|
|
50
|
+
return Intent(action).apply {
|
|
51
|
+
val types = mimeTypes
|
|
39
52
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
type =
|
|
54
|
+
if (types.size > 1) {
|
|
55
|
+
putExtra(Intent.EXTRA_MIME_TYPES, types)
|
|
56
|
+
intentFilterTypes
|
|
57
|
+
} else {
|
|
58
|
+
types[0]
|
|
59
|
+
}
|
|
60
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
|
61
|
+
initialDirectoryUrl != null
|
|
62
|
+
) {
|
|
63
|
+
// only works for ACTION_OPEN_DOCUMENT
|
|
64
|
+
// TODO must be URI
|
|
65
|
+
putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialDirectoryUrl)
|
|
66
|
+
}
|
|
67
|
+
if (!allowVirtualFiles) {
|
|
68
|
+
addCategory(Intent.CATEGORY_OPENABLE)
|
|
69
|
+
}
|
|
70
|
+
putExtra(Intent.EXTRA_LOCAL_ONLY, localOnly)
|
|
71
|
+
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
55
74
|
}
|
|
75
|
+
|
|
56
76
|
fun readableArrayToStringArray(readableArray: ReadableArray): Array<String> {
|
|
57
77
|
/**
|
|
58
78
|
* MIME type and Uri scheme matching in the
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// LICENSE: see License.md in the package root
|
|
2
|
+
package com.reactnativedocumentpicker
|
|
3
|
+
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.facebook.react.bridge.Promise
|
|
6
|
+
|
|
7
|
+
class PromiseWrapper(private val MODULE_NAME: String) {
|
|
8
|
+
private var promise: Promise? = null
|
|
9
|
+
private var nameOfCallInProgress: String? = null
|
|
10
|
+
|
|
11
|
+
fun trySetPromiseRejectingIncoming(promise: Promise, fromCallsite: String): Boolean {
|
|
12
|
+
val previousPromise = this.promise
|
|
13
|
+
if (previousPromise != null) {
|
|
14
|
+
rejectNewPromiseBecauseOldOneIsInProgress(promise, fromCallsite)
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
this.promise = promise
|
|
18
|
+
nameOfCallInProgress = fromCallsite
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fun resolve(value: Any?) {
|
|
23
|
+
val resolver = promise
|
|
24
|
+
if (resolver == null) {
|
|
25
|
+
Log.e(MODULE_NAME, "cannot resolve promise because it's null")
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
resetMembers()
|
|
30
|
+
resolver.resolve(value)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
fun reject(code: String, e: Exception) {
|
|
34
|
+
val message =
|
|
35
|
+
e.localizedMessage ?: e.message ?: "unknown error"
|
|
36
|
+
|
|
37
|
+
this.reject(code, message, e)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fun reject(e: Exception) {
|
|
41
|
+
val message =
|
|
42
|
+
e.localizedMessage ?: e.message ?: "unknown error"
|
|
43
|
+
|
|
44
|
+
this.reject(nameOfCallInProgress ?: "unknown call in progress", message, e)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fun rejectAsUserCancelledOperation() {
|
|
48
|
+
this.reject(E_DOCUMENT_PICKER_CANCELED, "user canceled the document picker")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
fun reject(code: String, message: String, e: Exception? = null) {
|
|
52
|
+
val rejecter = promise
|
|
53
|
+
if (rejecter == null) {
|
|
54
|
+
Log.e(MODULE_NAME, "cannot reject promise because it's null")
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
resetMembers()
|
|
59
|
+
rejecter.reject(code, message, e)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private fun resetMembers() {
|
|
63
|
+
nameOfCallInProgress = null
|
|
64
|
+
promise = null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private fun rejectNewPromiseBecauseOldOneIsInProgress(
|
|
68
|
+
promise: Promise,
|
|
69
|
+
requestedOperation: String?
|
|
70
|
+
) {
|
|
71
|
+
// TODO better message
|
|
72
|
+
promise.reject(
|
|
73
|
+
ASYNC_OP_IN_PROGRESS,
|
|
74
|
+
"Warning: previous promise did not settle and you attempted to overwrite it. " +
|
|
75
|
+
"You've called \"" + requestedOperation + "\" while \"" + this.nameOfCallInProgress + "\" was already in progress and has not completed yet."
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
companion object {
|
|
80
|
+
const val ASYNC_OP_IN_PROGRESS: String = "ASYNC_OP_IN_PROGRESS"
|
|
81
|
+
const val E_DOCUMENT_PICKER_CANCELED: String = "OPERATION_CANCELED"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -86,11 +86,11 @@ class RNDocumentPickerModule(reactContext: ReactApplicationContext) :
|
|
|
86
86
|
if (!promiseWrapper.trySetPromiseRejectingIncoming(promise, "pick")) {
|
|
87
87
|
return
|
|
88
88
|
}
|
|
89
|
-
val options =
|
|
89
|
+
val options = PickOptions(opts)
|
|
90
90
|
currentPickOptions = options
|
|
91
91
|
|
|
92
92
|
try {
|
|
93
|
-
val intent =
|
|
93
|
+
val intent = options.getPickIntent()
|
|
94
94
|
currentActivity.startActivityForResult(intent, PICK_FILES_REQUEST_CODE)
|
|
95
95
|
} catch (e: ActivityNotFoundException) {
|
|
96
96
|
promiseWrapper.reject(UNABLE_TO_OPEN_FILE_TYPE, e)
|
|
@@ -140,7 +140,7 @@ class RNDocumentPickerModule(reactContext: ReactApplicationContext) :
|
|
|
140
140
|
if (!promiseWrapper.trySetPromiseRejectingIncoming(promise, "pickDirectory")) {
|
|
141
141
|
return
|
|
142
142
|
}
|
|
143
|
-
val options =
|
|
143
|
+
val options = PickOptions(opts)
|
|
144
144
|
currentPickOptions = options
|
|
145
145
|
try {
|
|
146
146
|
val intent =
|
|
@@ -316,13 +316,13 @@ class RNDocumentPickerModule(reactContext: ReactApplicationContext) :
|
|
|
316
316
|
|
|
317
317
|
companion object {
|
|
318
318
|
fun rejectWithNullActivity(promise: Promise) {
|
|
319
|
-
promise.reject(
|
|
319
|
+
promise.reject(NULL_PRESENTER, "Current activity is null. Cannot present sign-in UI. Make sure there are no modal windows being presented or dismissed.")
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
private const val PICK_FILES_REQUEST_CODE = 41
|
|
323
323
|
private const val PICK_DIR_REQUEST_CODE = 42
|
|
324
324
|
private const val SAVE_DOC_REQUEST_CODE = 43
|
|
325
|
-
private const val
|
|
325
|
+
private const val NULL_PRESENTER = "NULL_PRESENTER"
|
|
326
326
|
private const val UNABLE_TO_OPEN_FILE_TYPE = "UNABLE_TO_OPEN_FILE_TYPE"
|
|
327
327
|
private const val E_OTHER_PRESENTING_ERROR = "OTHER_PRESENTING_ERROR"
|
|
328
328
|
private const val E_INVALID_DATA_RETURNED = "INVALID_DATA_RETURNED"
|
package/ios/RNDocumentPicker.mm
CHANGED
|
@@ -18,20 +18,23 @@
|
|
|
18
18
|
@end
|
|
19
19
|
|
|
20
20
|
@implementation RNDocumentPicker {
|
|
21
|
-
DocPicker *
|
|
22
|
-
DocSaver *
|
|
21
|
+
DocPicker *_docPicker;
|
|
22
|
+
DocSaver *_docSaver;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
// initialization happens on serial queue so there are no races
|
|
26
|
+
- (DocPicker *)docPicker {
|
|
27
|
+
if (!_docPicker) {
|
|
28
|
+
_docPicker = [DocPicker new];
|
|
29
29
|
}
|
|
30
|
-
return
|
|
30
|
+
return _docPicker;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
- (DocSaver *)docSaver {
|
|
34
|
+
if (!_docSaver) {
|
|
35
|
+
_docSaver = [DocSaver new];
|
|
36
|
+
}
|
|
37
|
+
return _docSaver;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
RCT_EXPORT_MODULE()
|
|
@@ -43,26 +46,9 @@ RCT_EXPORT_METHOD(pick:
|
|
|
43
46
|
reject:
|
|
44
47
|
(RCTPromiseRejectBlock) reject)
|
|
45
48
|
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
NSArray *allowedUTIs = [RCTConvert NSArray:options[@"type"]];
|
|
50
|
-
BOOL allowMultiple = [RCTConvert BOOL:options[@"allowMultiSelection"]];
|
|
51
|
-
BOOL showExtensions = [RCTConvert BOOL:options[@"showFileExtensions"]];
|
|
52
|
-
NSString *mode = options[@"mode"];
|
|
53
|
-
NSString *initialDir = options[@"initialDirectoryUrl"];
|
|
54
|
-
BOOL requestLongTermAccess = [RCTConvert BOOL:options[@"requestLongTermAccess"]];
|
|
55
|
-
|
|
56
|
-
PickerOptions *pickerOptions = [[PickerOptions alloc] initWithTypes:allowedUTIs
|
|
57
|
-
mode:mode
|
|
58
|
-
initialDirectoryUrl:initialDir
|
|
59
|
-
allowMultiSelection:allowMultiple
|
|
60
|
-
shouldShowFileExtensions:showExtensions
|
|
61
|
-
transitionStyle:transitionStyle
|
|
62
|
-
presentationStyle:presentationStyle
|
|
63
|
-
requestLongTermAccess:requestLongTermAccess];
|
|
64
|
-
|
|
65
|
-
[docPicker presentWithOptions:pickerOptions resolve:resolve reject:reject];
|
|
49
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
50
|
+
[self.docPicker presentWithOptionsDict:options resolve:resolve reject:reject];
|
|
51
|
+
});
|
|
66
52
|
}
|
|
67
53
|
|
|
68
54
|
RCT_EXPORT_METHOD(pickDirectory:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
|
|
@@ -92,21 +78,13 @@ RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSDictionary *, isKnownType:(NSString *)kind
|
|
|
92
78
|
}
|
|
93
79
|
|
|
94
80
|
RCT_EXPORT_METHOD(writeDocuments:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
BOOL asCopy = [RCTConvert BOOL:options[@"copy"]];
|
|
99
|
-
|
|
100
|
-
NSString *initialDir = options[@"initialDirectoryUri"];
|
|
101
|
-
NSArray<NSString*> *documentUrl = options[@"sourceUris"];
|
|
102
|
-
|
|
103
|
-
SaverOptions* saverOptions = [[SaverOptions alloc] initWithSourceUrlStrings:documentUrl asCopy:asCopy initialDirectoryUrl:initialDir shouldShowFileExtensions:showExtensions transitionStyle:transitionStyle presentationStyle:presentationStyle];
|
|
104
|
-
|
|
105
|
-
[docSaver presentWithOptions:saverOptions resolve:resolve reject:reject];
|
|
81
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
82
|
+
[self.docSaver presentWithOptionsDict:options resolve:resolve reject:reject];
|
|
83
|
+
});
|
|
106
84
|
}
|
|
107
85
|
|
|
108
86
|
RCT_EXPORT_METHOD(releaseSecureAccess:(NSArray *)uris resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
|
|
109
|
-
[docPicker stopAccessingOpenedUrls:uris];
|
|
87
|
+
[self.docPicker stopAccessingOpenedUrls:uris];
|
|
110
88
|
resolve([NSNull null]);
|
|
111
89
|
}
|
|
112
90
|
|