关于Android7.0调用系统相机拍照、访问相册的一些问题:
在Android6.0中Google提出了动态申请权限的Api,调用相机拍照,访问SDcard等操作都需要先申请对应的权限如下:
android:name="android.permission.CAMERA" />
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Google是反对放宽私有目录的访问权限的,所以收起对私有文件的访问权限是Android将来发展的趋势。
Android7.0中尝试传递 file:// URI 会触发 FileUriExposedException,因为在Android7.0之后Google认为直接使用本地的根目录即file:// URI是不安全的操作,直接访问会抛出FileUriExposedExCeption异常,这就意味着在Android7.0以前我们访问相机拍照存储时,如果使用URI的方式直接存储剪裁图片就会造成这个异常,那么如何解决这个问题呢?(文章结尾会附上demo的连接)
补充说明:本文Demo是直接在activity中访问相机、相册,发布后很多老哥留言说需要在Fragment中使用,为了大家使用方便,博主又增加了一个在fragment中调用的Demo,在文章结尾我会附上两个Demo的链接。
Google为我们提供了FileProvider类,进行一种特殊的内容提供,FileProvider时ContentProvide的子类,它使用了和内容提供器类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高了应用的安全性。下面就让我们看一下如何使用这个内容提供者进行数据访问的:
使用FileProvider获取Uri就会将以前的file:// URI准换成content:// URI,实现一种安全的应用间数据访问,内容提供者作为Android的四大组件之一,使用同样需要在清单文件AndroidManifest.xml中进行注册的,注册方法如下:
android:name="android.support.v4.content.FileProvider"
android:authorities="com.zz.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
provider标签里的 android:name的值是FileProvider的包名+类名为固定值。android:authorities的值相当于一个标志,当我们使用FileProvider的getUriForFile方法时的一个参数需和清单文件注册时的保持一致,这里我使用的是:com.zz.fileprovider可自行定义。exported:要求必须为false,为true则会报安全异常。grantUriPermissions:true,表示授予 URI 临时访问权限。
在res目录下创建xml文件夹,file_paths.xml文件内容如下:
xml version="1.0" encoding="utf-8"?>external-path标签用来指定Uri共享的,name属性的值可以自定义,path属性的值表示共享的具体位置,设置为空,就表示共享整个SD卡,也可指定对应的SDcard下的文件目录,根据需求自行定义。接下来就是调用系统相机进行拍照了,代码如下:name="camera_photos" path="" />
首先获取相机和访问SDcard权限:
/** * 自动获取相机权限 */ private void autoObtainCameraPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { ToastUtils.showShort(this, "您已经拒绝过一次"); } ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, CAMERA_PERMISSIONS_REQUEST_CODE); } else {//有权限直接调用系统相机拍照 if (hasSdcard()) { imageUri = Uri.fromFile(fileUri); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) imageUri = FileProvider.getUriForFile(MainActivity.this, "com.zz.fileprovider", fileUri);//通过FileProvider创建一个content类型的Uri PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_REQUEST); } else { ToastUtils.showShort(this, "设备没有SD卡!"); } } }
动态申请权限的回调方法:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case CAMERA_PERMISSIONS_REQUEST_CODE: {//调用系统相机申请拍照权限回调 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (hasSdcard()) { imageUri = Uri.fromFile(fileUri); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) imageUri = FileProvider.getUriForFile(MainActivity.this, "com.zz.fileprovider", fileUri);//通过FileProvider创建一个content类型的Uri PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_REQUEST); } else { ToastUtils.showShort(this, "设备没有SD卡!"); } } else { ToastUtils.showShort(this, "请允许打开相机!!"); } break; } case STORAGE_PERMISSIONS_REQUEST_CODE://调用系统相册申请Sdcard权限回调 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { PhotoUtils.openPic(this, CODE_GALLERY_REQUEST); } else { ToastUtils.showShort(this, "请允许打操作SDCard!!"); } break; } }*注:PhotoUtils是博主对拍照和相册获取照片的封装下面会给出,PhotoUtils类和完整的MainActivity类
重点看一下imageUri的获取,代码中imageUri是用于存储拍照后照片的Uri,调用相机拍照之前首先判断一下系统版本,AndroidN也就是Android7.0以上的系统通过FileProvider获取Uri方法的参数分别为,上下文对象、清单文件配置的android:authorities和对应的拍照存储的图片。之后就是通过PhotoUtils调用系统相机进行拍照。
PhotoUtils相应代码如下:
/** * @param activity 当前activity * @param imageUri 拍照后照片存储路径 * @param requestCode 调用系统相机请求码 */ public static void takePicture(Activity activity, Uri imageUri, int requestCode) { //调用系统相机 Intent intentCamera = new Intent(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件 } intentCamera.setAction(MediaStore.ACTION_IMAGE_CAPTURE); //将拍照结果保存至photo_file的Uri中,不保留在相册中 intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); activity.startActivityForResult(intentCamera, requestCode); }方法说明:通过Intent调用系统相机拍照,如果本机版本大于等于anroid7.0需要临时授权Uri的访问权限如下:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
其他部分和以前正常调用系统相机一样。
接下来就是对拍完的照片进行剪裁:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case CODE_CAMERA_REQUEST://拍照完成回调 cropImageUri = Uri.fromFile(fileCropUri); PhotoUtils.cropImageUri(this, imageUri, cropImageUri, 1, 1, output_X, output_Y, CODE_RESULT_REQUEST); break; case CODE_GALLERY_REQUEST://访问相册完成回调 if (hasSdcard()) { cropImageUri = Uri.fromFile(fileCropUri); Uri newUri = Uri.parse(PhotoUtils.getPath(this, data.getData())); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) newUri = FileProvider.getUriForFile(this, "com.zz.fileprovider", new File(newUri.getPath())); PhotoUtils.cropImageUri(this, newUri, cropImageUri, 1, 1, output_X, output_Y, CODE_RESULT_REQUEST); } else { ToastUtils.showShort(this, "设备没有SD卡!"); } break; case CODE_RESULT_REQUEST: Bitmap bitmap = PhotoUtils.getBitmapFromUri(cropImageUri, this); if (bitmap != null) { showImages(bitmap); } break; } } }剪裁代码如下:
/** * @param activity 当前activity * @param orgUri 剪裁原图的Uri * @param desUri 剪裁后的图片的Uri * @param aspectX X方向的比例 * @param aspectY Y方向的比例 * @param width 剪裁图片的宽度 * @param height 剪裁图片高度 * @param requestCode 剪裁图片的请求码 */ public static void cropImageUri(Activity activity, Uri orgUri, Uri desUri, int aspectX, int aspectY, int width, int height, int requestCode) { Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } intent.setDataAndType(orgUri, "image/*"); intent.putExtra("crop", "true"); intent.putExtra("aspectX", aspectX); intent.putExtra("aspectY", aspectY); intent.putExtra("outputX", width); intent.putExtra("outputY", height); intent.putExtra("scale", true); //将剪切的图片保存到目标Uri中 intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); activity.startActivityForResult(intent, requestCode); }和以前正常剪裁代码基本相同,和上面意图打开相机一样,如果本机版本大于等于anroid7.0需要临时授权Uri的访问权限如下:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
到此整体调用系统相机拍照、剪裁完成,剪裁后照片存储到cropImageUri对应路径。
访问相册代码如下:
/** * @param activity 当前activity * @param requestCode 打开相册的请求码 */ public static void openPic(Activity activity, int requestCode) { Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT); photoPickerIntent.setType("image/*"); activity.startActivityForResult(photoPickerIntent, requestCode); }上文中已贴出访问相册后的onActivityResult的回调,主要也是一样的操作解析Uri调用剪裁图片:
if (hasSdcard()) { cropImageUri = Uri.fromFile(fileCropUri); Uri newUri = Uri.parse(PhotoUtils.getPath(this, data.getData())); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) newUri = FileProvider.getUriForFile(this, "com.zz.fileprovider", new File(newUri.getPath())); PhotoUtils.cropImageUri(this, newUri, cropImageUri, 1, 1, output_X, output_Y, CODE_RESULT_REQUEST); } else { ToastUtils.showShort(this, "设备没有SD卡!"); } break;
*注:Android4.4之后选取中的图片不再返回真实的Uri了,而是封装过的Uri,所以在4.4以上,就要对这个Uri进行解析,即上面的PhotoUtils.getPath()方法,
具体Uri的解析见
PhotoUtils类。android4.4以前直接data.getData就可以获取到真是Uri不用解析。
解析获取真实的Uri后,判断系统版本开始通过FileProvider获取新的Uri之后就可以同样的图片剪裁了。
MainActivity代码如下:
package choicemmed.com.android60permissionsdemo; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Matrix; import android.media.ExifInterface; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import com.google.android.gms.common.api.GoogleApiClient; import com.lidroid.xutils.ViewUtils; import com.lidroid.xutils.view.annotation.ViewInject; import com.lidroid.xutils.view.annotation.event.OnClick; import java.io.File; import de.hdodenhof.circleimageview.CircleImageView; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final String TAG = "MainActivity"; @ViewInject(R.id.photo) private CircleImageView photo; @ViewInject(R.id.takePic) private Button takePic; @ViewInject(R.id.takeGallery) private Button takeGallery; @ViewInject(R.id.test) private Button test; private static final int CODE_GALLERY_REQUEST = 0xa0; private static final int CODE_CAMERA_REQUEST = 0xa1; private static final int CODE_RESULT_REQUEST = 0xa2; private static final int CAMERA_PERMISSIONS_REQUEST_CODE = 0x03; private static final int STORAGE_PERMISSIONS_REQUEST_CODE = 0x04; private File fileUri = new File(Environment.getExternalStorageDirectory().getPath() + "/photo.jpg"); private File fileCropUri = new File(Environment.getExternalStorageDirectory().getPath() + "/crop_photo.jpg"); private Uri imageUri; private Uri cropImageUri; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewUtils.inject(this); } @OnClick({R.id.takePic, R.id.takeGallery}) @Override public void onClick(View v) { switch (v.getId()) { case R.id.takePic: autoObtainCameraPermission(); break; case R.id.takeGallery: autoObtainStoragePermission(); break; } } /** * 自动获取相机权限 */ private void autoObtainCameraPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { ToastUtils.showShort(this, "您已经拒绝过一次"); } ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, CAMERA_PERMISSIONS_REQUEST_CODE); } else {//有权限直接调用系统相机拍照 if (hasSdcard()) { imageUri = Uri.fromFile(fileUri); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) imageUri = FileProvider.getUriForFile(MainActivity.this, "com.zz.fileprovider", fileUri);//通过FileProvider创建一个content类型的Uri PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_REQUEST); } else { ToastUtils.showShort(this, "设备没有SD卡!"); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case CAMERA_PERMISSIONS_REQUEST_CODE: {//调用系统相机申请拍照权限回调 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (hasSdcard()) { imageUri = Uri.fromFile(fileUri); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) imageUri = FileProvider.getUriForFile(MainActivity.this, "com.zz.fileprovider", fileUri);//通过FileProvider创建一个content类型的Uri PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_REQUEST); } else { ToastUtils.showShort(this, "设备没有SD卡!"); } } else { ToastUtils.showShort(this, "请允许打开相机!!"); } break; } case STORAGE_PERMISSIONS_REQUEST_CODE://调用系统相册申请Sdcard权限回调 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { PhotoUtils.openPic(this, CODE_GALLERY_REQUEST); } else { ToastUtils.showShort(this, "请允许打操作SDCard!!"); } break; } } private int output_X = 480; private int output_Y = 480; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case CODE_CAMERA_REQUEST://拍照完成回调 cropImageUri = Uri.fromFile(fileCropUri); PhotoUtils.cropImageUri(this, imageUri, cropImageUri, 1, 1, output_X, output_Y, CODE_RESULT_REQUEST); break; case CODE_GALLERY_REQUEST://访问相册完成回调 if (hasSdcard()) { cropImageUri = Uri.fromFile(fileCropUri); Uri newUri = Uri.parse(PhotoUtils.getPath(this, data.getData())); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) newUri = FileProvider.getUriForFile(this, "com.zz.fileprovider", new File(newUri.getPath())); PhotoUtils.cropImageUri(this, newUri, cropImageUri, 1, 1, output_X, output_Y, CODE_RESULT_REQUEST); } else { ToastUtils.showShort(this, "设备没有SD卡!"); } break; case CODE_RESULT_REQUEST: Bitmap bitmap = PhotoUtils.getBitmapFromUri(cropImageUri, this); if (bitmap != null) { showImages(bitmap); } break; } } } /** * 自动获取sdk权限 */ private void autoObtainStoragePermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSIONS_REQUEST_CODE); } else { PhotoUtils.openPic(this, CODE_GALLERY_REQUEST); } } private void showImages(Bitmap bitmap) { photo.setImageBitmap(bitmap); } /** * 检查设备是否存在SDCard的工具方法 */ public static boolean hasSdcard() { String state = Environment.getExternalStorageState(); return state.equals(Environment.MEDIA_MOUNTED); } }PhotoUtils代码如下:
package choicemmed.com.android60permissionsdemo; import android.annotation.SuppressLint; import android.app.Activity; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; /** * Created by:zheng zhong on 2016/8/6 16:16 * Email [email protected] */ public class PhotoUtils { private static final String TAG = "PhotoUtils"; /** * @param activity 当前activity * @param imageUri 拍照后照片存储路径 * @param requestCode 调用系统相机请求码 */ public static void takePicture(Activity activity, Uri imageUri, int requestCode) { //调用系统相机 Intent intentCamera = new Intent(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件 } intentCamera.setAction(MediaStore.ACTION_IMAGE_CAPTURE); //将拍照结果保存至photo_file的Uri中,不保留在相册中 intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); activity.startActivityForResult(intentCamera, requestCode); } /** * @param activity 当前activity * @param requestCode 打开相册的请求码 */ public static void openPic(Activity activity, int requestCode) { Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT); photoPickerIntent.setType("image/*"); activity.startActivityForResult(photoPickerIntent, requestCode); } /** * @param activity 当前activity * @param orgUri 剪裁原图的Uri * @param desUri 剪裁后的图片的Uri * @param aspectX X方向的比例 * @param aspectY Y方向的比例 * @param width 剪裁图片的宽度 * @param height 剪裁图片高度 * @param requestCode 剪裁图片的请求码 */ public static void cropImageUri(Activity activity, Uri orgUri, Uri desUri, int aspectX, int aspectY, int width, int height, int requestCode) { Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } intent.setDataAndType(orgUri, "image/*"); intent.putExtra("crop", "true"); intent.putExtra("aspectX", aspectX); intent.putExtra("aspectY", aspectY); intent.putExtra("outputX", width); intent.putExtra("outputY", height); intent.putExtra("scale", true); //将剪切的图片保存到目标Uri中 intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); activity.startActivityForResult(intent, requestCode); } /** * 读取uri所在的图片 * * @param uri 图片对应的Uri * @param mContext 上下文对象 * @return 获取图像的Bitmap */ public static Bitmap getBitmapFromUri(Uri uri, Context mContext) { try { Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), uri); return bitmap; } catch (Exception e) { e.printStackTrace(); return null; } } /** * @param context 上下文对象 * @param uri 当前相册照片的Uri * @return 解析后的Uri对应的String */ @SuppressLint("NewApi") public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; String pathHead = "file:///"; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return pathHead + Environment.getExternalStorageDirectory() + "/" + split[1]; } } // 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 pathHead + getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{split[1]}; return pathHead + getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { return pathHead + getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return pathHead + uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @param selection (Optional) Filter used in the query. * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ private static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ private static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ private static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } }