|
9 | 9 | import android.database.Cursor; |
10 | 10 | import android.net.Uri; |
11 | 11 | import android.os.Bundle; |
| 12 | +import android.os.Handler; |
| 13 | +import android.os.Looper; |
12 | 14 | import android.provider.DocumentsContract; |
13 | 15 | import android.provider.OpenableColumns; |
14 | 16 | import android.util.Log; |
|
18 | 20 | import com.facebook.react.bridge.ActivityEventListener; |
19 | 21 | import com.facebook.react.bridge.Arguments; |
20 | 22 | import com.facebook.react.bridge.BaseActivityEventListener; |
21 | | -import com.facebook.react.bridge.GuardedResultAsyncTask; |
22 | 23 | import com.facebook.react.bridge.Promise; |
23 | 24 | import com.facebook.react.bridge.ReactApplicationContext; |
24 | | -import com.facebook.react.bridge.ReactContext; |
25 | 25 | import com.facebook.react.bridge.ReactMethod; |
26 | 26 | import com.facebook.react.bridge.ReadableArray; |
27 | 27 | import com.facebook.react.bridge.ReadableMap; |
|
32 | 32 | import java.io.FileOutputStream; |
33 | 33 | import java.io.IOException; |
34 | 34 | import java.io.InputStream; |
35 | | -import java.lang.ref.WeakReference; |
36 | 35 | import java.util.ArrayList; |
37 | 36 | import java.util.List; |
38 | 37 | import java.util.UUID; |
| 38 | +import java.util.concurrent.Executor; |
| 39 | +import java.util.concurrent.Executors; |
39 | 40 |
|
40 | 41 | public class RNDocumentPickerModule extends NativeDocumentPickerSpec { |
| 42 | + private final Executor executor = Executors.newSingleThreadExecutor(); |
| 43 | + private final Handler handler = new Handler(Looper.getMainLooper()); |
| 44 | + private final ReactApplicationContext reactContext; |
41 | 45 | public static final String NAME = "RNDocumentPicker"; |
42 | 46 | private static final int READ_REQUEST_CODE = 41; |
43 | 47 | private static final int PICK_DIR_REQUEST_CODE = 42; |
@@ -66,6 +70,7 @@ public class RNDocumentPickerModule extends NativeDocumentPickerSpec { |
66 | 70 |
|
67 | 71 | public RNDocumentPickerModule(ReactApplicationContext reactContext) { |
68 | 72 | super(reactContext); |
| 73 | + this.reactContext = reactContext; |
69 | 74 | reactContext.addActivityEventListener(activityEventListener); |
70 | 75 | } |
71 | 76 |
|
@@ -136,7 +141,7 @@ public void pick(ReadableMap args, Promise promise) { |
136 | 141 | if (types.size() > 1) { |
137 | 142 | String[] mimeTypes = readableArrayToStringArray(types); |
138 | 143 | intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); |
139 | | - intent.setType(String.join("|",mimeTypes)); |
| 144 | + intent.setType(String.join("|", mimeTypes)); |
140 | 145 | } else if (types.size() == 1) { |
141 | 146 | intent.setType(types.getString(0)); |
142 | 147 | } |
@@ -222,128 +227,117 @@ public void onShowActivityResult(int resultCode, Intent data, Promise promise) { |
222 | 227 | sendError(E_INVALID_DATA_RETURNED, "Invalid data returned by intent"); |
223 | 228 | return; |
224 | 229 | } |
225 | | - |
226 | | - new ProcessDataTask(getReactApplicationContext(), uris, copyTo, promise).execute(); |
| 230 | + executor.execute(() -> { |
| 231 | + try { |
| 232 | + ReadableArray result = processData(uris); |
| 233 | + handler.post(() -> { |
| 234 | + promise.resolve(result); |
| 235 | + }); |
| 236 | + } catch (IOException e) { |
| 237 | + handler.post(() -> { |
| 238 | + promise.reject("DocumentPicker_ERROR", e); |
| 239 | + }); |
| 240 | + } |
| 241 | + }); |
227 | 242 | } catch (Exception e) { |
228 | 243 | sendError(E_UNEXPECTED_EXCEPTION, e.getLocalizedMessage(), e); |
229 | 244 | } |
230 | 245 | } |
231 | 246 |
|
232 | | - private static class ProcessDataTask extends GuardedResultAsyncTask<ReadableArray> { |
233 | | - private final WeakReference<Context> weakContext; |
234 | | - private final List<Uri> uris; |
235 | | - private final String copyTo; |
236 | | - private final Promise promise; |
237 | | - |
238 | | - protected ProcessDataTask(ReactContext reactContext, List<Uri> uris, String copyTo, Promise promise) { |
239 | | - super(reactContext.getExceptionHandler()); |
240 | | - this.weakContext = new WeakReference<>(reactContext.getApplicationContext()); |
241 | | - this.uris = uris; |
242 | | - this.copyTo = copyTo; |
243 | | - this.promise = promise; |
244 | | - } |
245 | | - |
246 | | - @Override |
247 | | - protected ReadableArray doInBackgroundGuarded() { |
248 | | - WritableArray results = Arguments.createArray(); |
249 | | - for (Uri uri : uris) { |
250 | | - results.pushMap(getMetadata(uri)); |
251 | | - } |
252 | | - return results; |
| 247 | + private ReadableArray processData(List<Uri> uris) throws IOException { |
| 248 | + WritableArray results = Arguments.createArray(); |
| 249 | + for (Uri uri : uris) { |
| 250 | + results.pushMap(getMetadata(uri)); |
253 | 251 | } |
| 252 | + return results; |
| 253 | + } |
254 | 254 |
|
255 | | - @Override |
256 | | - protected void onPostExecuteGuarded(ReadableArray readableArray) { |
257 | | - promise.resolve(readableArray); |
| 255 | + private WritableMap getMetadata(Uri uri) { |
| 256 | + Context context = reactContext; |
| 257 | + if (context == null) { |
| 258 | + return Arguments.createMap(); |
258 | 259 | } |
259 | | - |
260 | | - private WritableMap getMetadata(Uri uri) { |
261 | | - Context context = weakContext.get(); |
262 | | - if (context == null) { |
263 | | - return Arguments.createMap(); |
264 | | - } |
265 | | - ContentResolver contentResolver = context.getContentResolver(); |
266 | | - WritableMap map = Arguments.createMap(); |
267 | | - map.putString(FIELD_URI, uri.toString()); |
268 | | - map.putString(FIELD_TYPE, contentResolver.getType(uri)); |
269 | | - try (Cursor cursor = contentResolver.query(uri, null, null, null, null, null)) { |
270 | | - if (cursor != null && cursor.moveToFirst()) { |
271 | | - int displayNameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); |
272 | | - if (!cursor.isNull(displayNameIndex)) { |
273 | | - String fileName = cursor.getString(displayNameIndex); |
274 | | - map.putString(FIELD_NAME, fileName); |
275 | | - } else { |
276 | | - map.putNull(FIELD_NAME); |
277 | | - } |
278 | | - int mimeIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_MIME_TYPE); |
279 | | - if (!cursor.isNull(mimeIndex)) { |
280 | | - map.putString(FIELD_TYPE, cursor.getString(mimeIndex)); |
281 | | - } |
282 | | - int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE); |
283 | | - if (cursor.isNull(sizeIndex)) { |
284 | | - map.putNull(FIELD_SIZE); |
285 | | - } else { |
286 | | - map.putDouble(FIELD_SIZE, cursor.getLong(sizeIndex)); |
287 | | - } |
| 260 | + ContentResolver contentResolver = context.getContentResolver(); |
| 261 | + WritableMap map = Arguments.createMap(); |
| 262 | + map.putString(FIELD_URI, uri.toString()); |
| 263 | + map.putString(FIELD_TYPE, contentResolver.getType(uri)); |
| 264 | + try (Cursor cursor = contentResolver.query(uri, null, null, null, null, null)) { |
| 265 | + if (cursor != null && cursor.moveToFirst()) { |
| 266 | + int displayNameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); |
| 267 | + if (!cursor.isNull(displayNameIndex)) { |
| 268 | + String fileName = cursor.getString(displayNameIndex); |
| 269 | + map.putString(FIELD_NAME, fileName); |
| 270 | + } else { |
| 271 | + map.putNull(FIELD_NAME); |
| 272 | + } |
| 273 | + int mimeIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_MIME_TYPE); |
| 274 | + if (!cursor.isNull(mimeIndex)) { |
| 275 | + map.putString(FIELD_TYPE, cursor.getString(mimeIndex)); |
| 276 | + } |
| 277 | + int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE); |
| 278 | + if (cursor.isNull(sizeIndex)) { |
| 279 | + map.putNull(FIELD_SIZE); |
| 280 | + } else { |
| 281 | + map.putDouble(FIELD_SIZE, cursor.getLong(sizeIndex)); |
288 | 282 | } |
289 | 283 | } |
290 | | - |
291 | | - prepareFileUri(context, map, uri); |
292 | | - return map; |
293 | 284 | } |
294 | 285 |
|
295 | | - private void prepareFileUri(Context context, WritableMap map, Uri uri) { |
296 | | - if (copyTo == null) { |
297 | | - map.putNull(FIELD_FILE_COPY_URI); |
298 | | - } else { |
299 | | - copyFileToLocalStorage(context, map, uri); |
300 | | - } |
| 286 | + prepareFileUri(context, map, uri); |
| 287 | + return map; |
| 288 | + } |
| 289 | + |
| 290 | + private void prepareFileUri(Context context, WritableMap map, Uri uri) { |
| 291 | + if (copyTo == null) { |
| 292 | + map.putNull(FIELD_FILE_COPY_URI); |
| 293 | + } else { |
| 294 | + copyFileToLocalStorage(context, map, uri); |
301 | 295 | } |
| 296 | + } |
302 | 297 |
|
303 | | - private void copyFileToLocalStorage(Context context, WritableMap map, Uri uri) { |
304 | | - File dir = context.getCacheDir(); |
305 | | - if (copyTo.equals("documentDirectory")) { |
306 | | - dir = context.getFilesDir(); |
| 298 | + private void copyFileToLocalStorage(Context context, WritableMap map, Uri uri) { |
| 299 | + File dir = context.getCacheDir(); |
| 300 | + if (copyTo.equals("documentDirectory")) { |
| 301 | + dir = context.getFilesDir(); |
| 302 | + } |
| 303 | + // we don't want to rename the file so we put it into a unique location |
| 304 | + dir = new File(dir, UUID.randomUUID().toString()); |
| 305 | + try { |
| 306 | + boolean didCreateDir = dir.mkdir(); |
| 307 | + if (!didCreateDir) { |
| 308 | + throw new IOException("failed to create directory at " + dir.getAbsolutePath()); |
307 | 309 | } |
308 | | - // we don't want to rename the file so we put it into a unique location |
309 | | - dir = new File(dir, UUID.randomUUID().toString()); |
310 | | - try { |
311 | | - boolean didCreateDir = dir.mkdir(); |
312 | | - if (!didCreateDir) { |
313 | | - throw new IOException("failed to create directory at " + dir.getAbsolutePath()); |
314 | | - } |
315 | | - String fileName = map.getString(FIELD_NAME); |
316 | | - if (fileName == null) { |
317 | | - fileName = String.valueOf(System.currentTimeMillis()); |
318 | | - } |
319 | | - File destFile = safeGetDestination(new File(dir, fileName), dir.getCanonicalPath()); |
320 | | - Uri copyPath = copyFile(context, uri, destFile); |
321 | | - map.putString(FIELD_FILE_COPY_URI, copyPath.toString()); |
322 | | - } catch (Exception e) { |
323 | | - e.printStackTrace(); |
324 | | - map.putNull(FIELD_FILE_COPY_URI); |
325 | | - map.putString(FIELD_COPY_ERROR, e.getLocalizedMessage()); |
| 310 | + String fileName = map.getString(FIELD_NAME); |
| 311 | + if (fileName == null) { |
| 312 | + fileName = String.valueOf(System.currentTimeMillis()); |
326 | 313 | } |
| 314 | + File destFile = safeGetDestination(new File(dir, fileName), dir.getCanonicalPath()); |
| 315 | + Uri copyPath = copyFile(context, uri, destFile); |
| 316 | + map.putString(FIELD_FILE_COPY_URI, copyPath.toString()); |
| 317 | + } catch (Exception e) { |
| 318 | + e.printStackTrace(); |
| 319 | + map.putNull(FIELD_FILE_COPY_URI); |
| 320 | + map.putString(FIELD_COPY_ERROR, e.getLocalizedMessage()); |
327 | 321 | } |
| 322 | + } |
328 | 323 |
|
329 | | - public File safeGetDestination(File destFile, String expectedDir) throws IllegalArgumentException, IOException { |
330 | | - String canonicalPath = destFile.getCanonicalPath(); |
331 | | - if (!canonicalPath.startsWith(expectedDir)) { |
332 | | - throw new IllegalArgumentException("The copied file is attempting to write outside of the target directory."); |
333 | | - } |
334 | | - return destFile; |
| 324 | + public File safeGetDestination(File destFile, String expectedDir) throws IllegalArgumentException, IOException { |
| 325 | + String canonicalPath = destFile.getCanonicalPath(); |
| 326 | + if (!canonicalPath.startsWith(expectedDir)) { |
| 327 | + throw new IllegalArgumentException("The copied file is attempting to write outside of the target directory."); |
335 | 328 | } |
| 329 | + return destFile; |
| 330 | + } |
336 | 331 |
|
337 | | - public static Uri copyFile(Context context, Uri uri, File destFile) throws IOException { |
338 | | - try(InputStream inputStream = context.getContentResolver().openInputStream(uri); |
339 | | - FileOutputStream outputStream = new FileOutputStream(destFile)) { |
340 | | - byte[] buf = new byte[8192]; |
341 | | - int len; |
342 | | - while ((len = inputStream.read(buf)) > 0) { |
343 | | - outputStream.write(buf, 0, len); |
344 | | - } |
345 | | - return Uri.fromFile(destFile); |
| 332 | + public static Uri copyFile(Context context, Uri uri, File destFile) throws IOException { |
| 333 | + try (InputStream inputStream = context.getContentResolver().openInputStream(uri); |
| 334 | + FileOutputStream outputStream = new FileOutputStream(destFile)) { |
| 335 | + byte[] buf = new byte[8192]; |
| 336 | + int len; |
| 337 | + while ((len = inputStream.read(buf)) > 0) { |
| 338 | + outputStream.write(buf, 0, len); |
346 | 339 | } |
| 340 | + return Uri.fromFile(destFile); |
347 | 341 | } |
348 | 342 | } |
349 | 343 |
|
|
0 commit comments