* Dev dependecies * upgrade tinycolor2 * upgrade rn-placeholder * upgrade react-native@0.63.3 * Upgrade react-native-vector-icons * Upgrade react-native-screens * Upgrade react-native-safe-area-context * Upgrade react-native-reanimated * Upgrade react-native-permissions * Upgrade react-native-localize * Upgrade react-native-image-picker * Upgrade react-native-haptic-feedback * Upgrade react-native-gesture-handler * Upgrade react-native-file-viewer * Upgrade react-native-document-picker * Upgrade react-native-device-info * Upgrade react-native-calendars * Upgrade mime-db * Upgrade jail-monkey * Upgrade fuse.js * Upgrade emoji-regex * Upgrade deep-equal * Upgrade @react-navigation * Upgrade @react-native-community/netinfo * Upgrade @react-native-community/clipboard * Upgrade @react-native-community/cameraroll * Upgrade @react-native-community/async-storage * Upgrade @sentry/react-native * Upgrade @types/react-native * Upgrade jest
476 lines
18 KiB
Diff
476 lines
18 KiB
Diff
diff --git a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java
|
|
index 48fb5c1..3872d91 100644
|
|
--- a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java
|
|
+++ b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/ImagePickerModule.java
|
|
@@ -3,6 +3,7 @@ package com.imagepicker;
|
|
import android.Manifest;
|
|
import android.app.Activity;
|
|
import android.content.ActivityNotFoundException;
|
|
+import android.content.ContentResolver;
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.Intent;
|
|
@@ -49,6 +50,7 @@ import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.lang.ref.WeakReference;
|
|
import java.util.List;
|
|
+import java.util.ArrayList;
|
|
|
|
import com.facebook.react.modules.core.PermissionListener;
|
|
import com.facebook.react.modules.core.PermissionAwareActivity;
|
|
@@ -69,6 +71,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
public static final int REQUEST_LAUNCH_IMAGE_LIBRARY = 13002;
|
|
public static final int REQUEST_LAUNCH_VIDEO_LIBRARY = 13003;
|
|
public static final int REQUEST_LAUNCH_VIDEO_CAPTURE = 13004;
|
|
+ public static final int REQUEST_LAUNCH_MIXED_CAPTURE = 13005;
|
|
public static final int REQUEST_PERMISSIONS_FOR_CAMERA = 14001;
|
|
public static final int REQUEST_PERMISSIONS_FOR_LIBRARY = 14002;
|
|
|
|
@@ -266,26 +269,24 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
cameraIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, videoDurationLimit);
|
|
}
|
|
}
|
|
+ else if (pickBoth) {
|
|
+ Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
|
+ this.setImageCaptureUri(takePictureIntent);
|
|
+ Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
|
|
+ cameraIntent = new Intent(Intent.ACTION_CHOOSER);
|
|
+ Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
+ Intent[] intentArray = new Intent[]{takePictureIntent,takeVideoIntent};
|
|
+ cameraIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
|
|
+ cameraIntent.putExtra(Intent.EXTRA_TITLE, "Choose an action");
|
|
+ cameraIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
|
|
+ requestCode = REQUEST_LAUNCH_MIXED_CAPTURE;
|
|
+ }
|
|
else
|
|
{
|
|
requestCode = REQUEST_LAUNCH_IMAGE_CAPTURE;
|
|
cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
|
|
|
- final File original = createNewFile(reactContext, this.options, false);
|
|
- imageConfig = imageConfig.withOriginalFile(original);
|
|
-
|
|
- if (imageConfig.original != null) {
|
|
- cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original);
|
|
- }else {
|
|
- responseHelper.invokeError(callback, "Couldn't get file path for photo");
|
|
- return;
|
|
- }
|
|
- if (cameraCaptureURI == null)
|
|
- {
|
|
- responseHelper.invokeError(callback, "Couldn't get file path for photo");
|
|
- return;
|
|
- }
|
|
- cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraCaptureURI);
|
|
+ this.setImageCaptureUri(cameraIntent);
|
|
}
|
|
|
|
if (cameraIntent.resolveActivity(reactContext.getPackageManager()) == null)
|
|
@@ -350,16 +351,19 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
libraryIntent = new Intent(Intent.ACTION_PICK);
|
|
libraryIntent.setType("video/*");
|
|
}
|
|
+ else if (pickBoth) {
|
|
+ libraryIntent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
+ libraryIntent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
+ libraryIntent.setType("image/*");
|
|
+ libraryIntent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {"image/*", "video/*"});
|
|
+ requestCode = REQUEST_LAUNCH_MIXED_CAPTURE;
|
|
+ }
|
|
else
|
|
{
|
|
requestCode = REQUEST_LAUNCH_IMAGE_LIBRARY;
|
|
libraryIntent = new Intent(Intent.ACTION_PICK,
|
|
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
|
-
|
|
- if (pickBoth)
|
|
- {
|
|
- libraryIntent.setType("image/* video/*");
|
|
- }
|
|
+ libraryIntent.setType("image/*");
|
|
}
|
|
|
|
if (libraryIntent.resolveActivity(reactContext.getPackageManager()) == null)
|
|
@@ -385,75 +389,47 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
}
|
|
}
|
|
|
|
- @Override
|
|
- public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
|
- //robustness code
|
|
- if (passResult(requestCode))
|
|
- {
|
|
- return;
|
|
- }
|
|
-
|
|
- responseHelper.cleanResponse();
|
|
-
|
|
- // user cancel
|
|
- if (resultCode != Activity.RESULT_OK)
|
|
- {
|
|
- removeUselessFiles(requestCode, imageConfig);
|
|
- responseHelper.invokeCancel(callback);
|
|
- callback = null;
|
|
- return;
|
|
- }
|
|
+ protected String getMimeType(Activity activity, Uri uri) {
|
|
+ String mimeType = null;
|
|
+ if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
|
|
+ ContentResolver cr = activity.getApplicationContext().getContentResolver();
|
|
+ mimeType = cr.getType(uri);
|
|
+ } else {
|
|
+ String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
|
|
+ .toString());
|
|
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
|
+ fileExtension.toLowerCase());
|
|
+ }
|
|
+ return mimeType;
|
|
+ }
|
|
|
|
- Uri uri = null;
|
|
- switch (requestCode)
|
|
+ protected void extractImageFromResult(Activity activity, Uri uri, int requestCode) {
|
|
+ String realPath = getRealPathFromURI(uri);
|
|
+ String mime = getMimeType(activity, uri);
|
|
+ final boolean isUrl = !TextUtils.isEmpty(realPath) &&
|
|
+ Patterns.WEB_URL.matcher(realPath).matches();
|
|
+ if (realPath == null || isUrl)
|
|
{
|
|
- case REQUEST_LAUNCH_IMAGE_CAPTURE:
|
|
- uri = cameraCaptureURI;
|
|
- break;
|
|
-
|
|
- case REQUEST_LAUNCH_IMAGE_LIBRARY:
|
|
- uri = data.getData();
|
|
- String realPath = getRealPathFromURI(uri);
|
|
- final boolean isUrl = !TextUtils.isEmpty(realPath) &&
|
|
- Patterns.WEB_URL.matcher(realPath).matches();
|
|
- if (realPath == null || isUrl)
|
|
- {
|
|
- try
|
|
- {
|
|
- File file = createFileFromURI(uri);
|
|
- realPath = file.getAbsolutePath();
|
|
- uri = Uri.fromFile(file);
|
|
- }
|
|
- catch (Exception e)
|
|
- {
|
|
- // image not in cache
|
|
- responseHelper.putString("error", "Could not read photo");
|
|
- responseHelper.putString("uri", uri.toString());
|
|
- responseHelper.invokeResponse(callback);
|
|
- callback = null;
|
|
- return;
|
|
- }
|
|
- }
|
|
- imageConfig = imageConfig.withOriginalFile(new File(realPath));
|
|
- break;
|
|
-
|
|
- case REQUEST_LAUNCH_VIDEO_LIBRARY:
|
|
- responseHelper.putString("uri", data.getData().toString());
|
|
- responseHelper.putString("path", getRealPathFromURI(data.getData()));
|
|
- responseHelper.invokeResponse(callback);
|
|
- callback = null;
|
|
- return;
|
|
-
|
|
- case REQUEST_LAUNCH_VIDEO_CAPTURE:
|
|
- final String path = getRealPathFromURI(data.getData());
|
|
- responseHelper.putString("uri", data.getData().toString());
|
|
- responseHelper.putString("path", path);
|
|
- fileScan(reactContext, path);
|
|
+ try
|
|
+ {
|
|
+ File file = createFileFromURI(uri);
|
|
+ realPath = file.getAbsolutePath();
|
|
+ uri = Uri.fromFile(file);
|
|
+ }
|
|
+ catch (Exception e)
|
|
+ {
|
|
+ // image not in cache
|
|
+ responseHelper.putString("error", "Could not read photo");
|
|
+ responseHelper.putString("uri", uri.toString());
|
|
responseHelper.invokeResponse(callback);
|
|
callback = null;
|
|
+ this.options = null;
|
|
return;
|
|
+ }
|
|
}
|
|
|
|
+ imageConfig = imageConfig.withOriginalFile(new File(realPath));
|
|
+
|
|
final ReadExifResult result = readExifInterface(responseHelper, imageConfig);
|
|
|
|
if (result.error != null)
|
|
@@ -461,6 +437,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
removeUselessFiles(requestCode, imageConfig);
|
|
responseHelper.invokeError(callback, result.error.getMessage());
|
|
callback = null;
|
|
+ this.options = null;
|
|
return;
|
|
}
|
|
|
|
@@ -472,7 +449,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
updatedResultResponse(uri, imageConfig.original.getAbsolutePath());
|
|
|
|
// don't create a new file if contraint are respected
|
|
- if (imageConfig.useOriginal(initialWidth, initialHeight, result.currentRotation))
|
|
+ if (imageConfig.useOriginal(initialWidth, initialHeight, result.currentRotation) || mime.equals("image/gif"))
|
|
{
|
|
responseHelper.putInt("width", initialWidth);
|
|
responseHelper.putInt("height", initialHeight);
|
|
@@ -481,6 +458,14 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
else
|
|
{
|
|
imageConfig = getResizedImage(reactContext, this.options, imageConfig, initialWidth, initialHeight, requestCode);
|
|
+ if (imageConfig == null)
|
|
+ {
|
|
+ responseHelper.invokeError(callback, "Could not read image");
|
|
+ callback = null;
|
|
+ this.options = null;
|
|
+ return;
|
|
+ }
|
|
+
|
|
if (imageConfig.resized == null)
|
|
{
|
|
removeUselessFiles(requestCode, imageConfig);
|
|
@@ -523,6 +508,64 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
this.options = null;
|
|
}
|
|
|
|
+ protected void extractVideoFromResult(Uri uri) {
|
|
+ responseHelper.putString("uri", uri.toString());
|
|
+ responseHelper.putString("path", getRealPathFromURI(uri));
|
|
+ responseHelper.invokeResponse(callback);
|
|
+ callback = null;
|
|
+ this.options = null;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
|
+ //robustness code
|
|
+ if (passResult(requestCode))
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ responseHelper.cleanResponse();
|
|
+
|
|
+ // user cancel
|
|
+ if (resultCode != Activity.RESULT_OK)
|
|
+ {
|
|
+ removeUselessFiles(requestCode, imageConfig);
|
|
+ responseHelper.invokeCancel(callback);
|
|
+ callback = null;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ switch (requestCode)
|
|
+ {
|
|
+ case REQUEST_LAUNCH_IMAGE_CAPTURE:
|
|
+ extractImageFromResult(activity, cameraCaptureURI, requestCode);
|
|
+ break;
|
|
+
|
|
+ case REQUEST_LAUNCH_IMAGE_LIBRARY:
|
|
+ extractImageFromResult(activity, data.getData(), requestCode);
|
|
+ break;
|
|
+
|
|
+ case REQUEST_LAUNCH_VIDEO_LIBRARY:
|
|
+ extractVideoFromResult(data.getData());
|
|
+ break;
|
|
+
|
|
+ case REQUEST_LAUNCH_MIXED_CAPTURE:
|
|
+ case REQUEST_LAUNCH_VIDEO_CAPTURE:
|
|
+ if (data == null || data.getData() == null) {
|
|
+ extractImageFromResult(activity, cameraCaptureURI, requestCode);
|
|
+ } else {
|
|
+ Uri selectedMediaUri = data.getData();
|
|
+ String mime = getMimeType(activity, selectedMediaUri);
|
|
+ if (mime.contains("image")) {
|
|
+ extractImageFromResult(activity, selectedMediaUri, requestCode);
|
|
+ } else {
|
|
+ extractVideoFromResult(selectedMediaUri);
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
public void invokeCustomButton(@NonNull final String action)
|
|
{
|
|
responseHelper.invokeCustomButton(this.callback, action);
|
|
@@ -551,7 +594,8 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
{
|
|
return callback == null || (cameraCaptureURI == null && requestCode == REQUEST_LAUNCH_IMAGE_CAPTURE)
|
|
|| (requestCode != REQUEST_LAUNCH_IMAGE_CAPTURE && requestCode != REQUEST_LAUNCH_IMAGE_LIBRARY
|
|
- && requestCode != REQUEST_LAUNCH_VIDEO_LIBRARY && requestCode != REQUEST_LAUNCH_VIDEO_CAPTURE);
|
|
+ && requestCode != REQUEST_LAUNCH_VIDEO_LIBRARY && requestCode != REQUEST_LAUNCH_VIDEO_CAPTURE
|
|
+ && requestCode != REQUEST_LAUNCH_MIXED_CAPTURE);
|
|
}
|
|
|
|
private void updatedResultResponse(@Nullable final Uri uri,
|
|
@@ -571,22 +615,23 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
@NonNull final Callback callback,
|
|
@NonNull final int requestCode)
|
|
{
|
|
- final int writePermission = ActivityCompat
|
|
- .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
|
- final int cameraPermission = ActivityCompat
|
|
- .checkSelfPermission(activity, Manifest.permission.CAMERA);
|
|
-
|
|
- boolean permissionsGranted = false;
|
|
-
|
|
+ int selfCheckResult = 0;
|
|
switch (requestCode) {
|
|
case REQUEST_PERMISSIONS_FOR_LIBRARY:
|
|
- permissionsGranted = writePermission == PackageManager.PERMISSION_GRANTED;
|
|
+ selfCheckResult = ActivityCompat
|
|
+ .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
|
break;
|
|
case REQUEST_PERMISSIONS_FOR_CAMERA:
|
|
- permissionsGranted = cameraPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED;
|
|
+ selfCheckResult = ActivityCompat
|
|
+ .checkSelfPermission(activity, Manifest.permission.CAMERA);
|
|
+ if (selfCheckResult == PackageManager.PERMISSION_GRANTED) {
|
|
+ selfCheckResult = ActivityCompat
|
|
+ .checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
|
+ }
|
|
break;
|
|
}
|
|
|
|
+ final boolean permissionsGranted = selfCheckResult == PackageManager.PERMISSION_GRANTED;
|
|
if (!permissionsGranted)
|
|
{
|
|
final Boolean dontAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA);
|
|
@@ -787,4 +832,22 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
|
|
videoDurationLimit = options.getInt("durationLimit");
|
|
}
|
|
}
|
|
+
|
|
+ private void setImageCaptureUri(Intent cameraIntent) {
|
|
+ final File original = createNewFile(reactContext, this.options, false);
|
|
+ imageConfig = imageConfig.withOriginalFile(original);
|
|
+
|
|
+ if (imageConfig.original != null) {
|
|
+ cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, imageConfig.original);
|
|
+ }else {
|
|
+ responseHelper.invokeError(callback, "Couldn't get file path for photo");
|
|
+ return;
|
|
+ }
|
|
+ if (cameraCaptureURI == null)
|
|
+ {
|
|
+ responseHelper.invokeError(callback, "Couldn't get file path for photo");
|
|
+ return;
|
|
+ }
|
|
+ cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraCaptureURI);
|
|
+ }
|
|
}
|
|
diff --git a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java
|
|
index cc90dce..c1cc74d 100644
|
|
--- a/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java
|
|
+++ b/node_modules/react-native-image-picker/android/src/main/java/com/imagepicker/utils/RealPathUtil.java
|
|
@@ -7,16 +7,22 @@ import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.provider.DocumentsContract;
|
|
import android.provider.MediaStore;
|
|
+import android.provider.OpenableColumns;
|
|
import android.content.ContentUris;
|
|
import android.os.Environment;
|
|
+import android.os.ParcelFileDescriptor;
|
|
+import android.webkit.MimeTypeMap;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.core.content.FileProvider;
|
|
|
|
-import java.io.File;
|
|
+import java.io.*;
|
|
+import java.nio.channels.FileChannel;
|
|
|
|
public class RealPathUtil {
|
|
|
|
+ public static final String CACHE_DIR_NAME = "mmShare";
|
|
+
|
|
public static @Nullable Uri compatUriFromFile(@NonNull final Context context,
|
|
@NonNull final File file) {
|
|
Uri result = null;
|
|
@@ -58,12 +64,7 @@ public class RealPathUtil {
|
|
}
|
|
// DownloadsProvider
|
|
else if (isDownloadsDocument(uri)) {
|
|
-
|
|
- final String id = DocumentsContract.getDocumentId(uri);
|
|
- final Uri contentUri = ContentUris.withAppendedId(
|
|
- Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
|
-
|
|
- return getDataColumn(context, contentUri, null, null);
|
|
+ return getPathFromSavingTempFile(context, uri);
|
|
}
|
|
// MediaProvider
|
|
else if (isMediaDocument(uri)) {
|
|
@@ -89,7 +90,7 @@ public class RealPathUtil {
|
|
}
|
|
}
|
|
// MediaStore (and general)
|
|
- else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
|
+ if ("content".equalsIgnoreCase(uri.getScheme())) {
|
|
|
|
// Return the remote address
|
|
if (isGooglePhotosUri(uri))
|
|
@@ -98,7 +99,7 @@ public class RealPathUtil {
|
|
if (isFileProviderUri(context, uri))
|
|
return getFileProviderPath(context, uri);
|
|
|
|
- return getDataColumn(context, uri, null, null);
|
|
+ return getPathFromSavingTempFile(context, uri);
|
|
}
|
|
// File
|
|
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
|
@@ -108,6 +109,49 @@ public class RealPathUtil {
|
|
return null;
|
|
}
|
|
|
|
+
|
|
+ public static String getPathFromSavingTempFile(Context context, final Uri uri) {
|
|
+ File tmpFile;
|
|
+ String fileName = null;
|
|
+
|
|
+ // Try and get the filename from the Uri
|
|
+ try {
|
|
+ Cursor returnCursor =
|
|
+ context.getContentResolver().query(uri, null, null, null, null);
|
|
+ int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
|
+ returnCursor.moveToFirst();
|
|
+ fileName = returnCursor.getString(nameIndex);
|
|
+ } catch (Exception e) {
|
|
+ // just continue to get the filename with the last segment of the path
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ if (fileName == null) {
|
|
+ fileName = uri.getLastPathSegment().toString().trim();
|
|
+ }
|
|
+
|
|
+
|
|
+ File cacheDir = new File(context.getCacheDir(), CACHE_DIR_NAME);
|
|
+ if (!cacheDir.exists()) {
|
|
+ cacheDir.mkdirs();
|
|
+ }
|
|
+
|
|
+ tmpFile = new File(cacheDir, fileName);
|
|
+ tmpFile.createNewFile();
|
|
+
|
|
+ ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");
|
|
+
|
|
+ FileChannel src = new FileInputStream(pfd.getFileDescriptor()).getChannel();
|
|
+ FileChannel dst = new FileOutputStream(tmpFile).getChannel();
|
|
+ dst.transferFrom(src, 0, src.size());
|
|
+ src.close();
|
|
+ dst.close();
|
|
+ } catch (IOException ex) {
|
|
+ return null;
|
|
+ }
|
|
+ return tmpFile.getAbsolutePath();
|
|
+ }
|
|
+
|
|
/**
|
|
* Get the value of the data column for this Uri. This is useful for
|
|
* MediaStore Uris, and other file-based ContentProviders.
|