The Exception is:
file:// Uri exposed through ClipData.Item.getUri()
java.lang.Throwable: file:// Uri exposed through ClipData.Item.getUri()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1618)
at android.net.Uri.checkFileUriExposed(Uri.java:2341)
at android.content.ClipData.prepareToLeaveProcess(ClipData.java:808)
at android.content.Intent.prepareToLeaveProcess(Intent.java:7926)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1506)
at android.app.Activity.startActivityForResult(Activity.java:3832)
at android.app.Activity.startActivityForResult(Activity.java:3783)
at android.support.v4.app.FragmentActivity.startActivityFromFragment(Unknown Source)
at android.support.v4.app.Fragment.startActivityForResult(Unknown Source)
at me.chunyu.ChunyuDoctor.Utility.w.takePhoto(Unknown Source)
at me.chunyu.ChunyuDoctor.Dialog.ChoosePhotoDialogFragment.takePhoto(Unknown Source)
at me.chunyu.ChunyuDoctor.Dialog.ChoosePhotoDialogFragment.access$000(Unknown Source)
at me.chunyu.ChunyuDoctor.Dialog.b.onClick(Unknown Source)
at me.chunyu.ChunyuDoctor.Dialog.ChoiceDialogFragment.onClick(Unknown Source)
at android.view.View.performClick(View.java:4848)
at android.view.View$PerformClick.run(View.java:20270)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5643)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:960)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
My code is here:
public static void takePhoto(Fragment fragment, int token, Uri uri) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (uri != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
}
fragment.startActivityForResult(intent, token);
}
I searched the similar problems and solutions.
And modify the code as follow:
public static void takePhoto(Fragment fragment, int token, Uri uri) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (uri != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
}
fragment.startActivityForResult(intent, token);
}
But it is also not work.
It happend on Android 5.1 While work well on Android 4.3.
Is there anyone meet the same problem?
Ask for some advance.
Waiting online...
I have already resolved this problem.
First, this problem occurred because StrictMode
prevents passing URIs with a file://
scheme.
So there are two solutions:
Change StrictMode
. See similar problem and its code.
But for our apps, it is not realistic to modify the Android source code.
Use another URI scheme, instead of file://
. For example, content://
related to MediaStore
.
So I chose the second method:
private void doTakePhoto() {
try {
ContentValues values = new ContentValues(1);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
mCameraTempUri = getActivity().getContentResolver()
.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
takePhoto(this, RequestCode.REQCODE_TAKE_PHOTO, mCameraTempUri);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void takePhoto(Fragment fragment, int token, Uri uri) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
if (uri != null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
}
fragment.startActivityForResult(intent, token);
}
Also, there is another solution.
So, I was actually reading about this, and it seems the correct solution to handle this is the following:
String mCurrentPhotoPath;
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
return image;
}
static final int REQUEST_TAKE_PHOTO = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
...
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.android.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
Notice there is a note that google says to create a "content://" file instead of a "file://" based resource.
This is from google:
Note: We are using getUriForFile(Context, String, File) which returns a content:// URI. For more recent apps targeting Android N and higher, passing a file:// URI across a package boundary causes a FileUriExposedException. Therefore, we now present a more generic way of storing images using a FileProvider.
Also, you will need to setup the following:
Now, you need to configure the FileProvider. In your app's manifest, add a provider to your application:
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
</provider>
...
</application>
Note: (Taken from google's site) Make sure that the authorities string matches the second argument to getUriForFile(Context, String, File). In the meta-data section of the provider definition, you can see that the provider expects eligible paths to be configured in a dedicated resource file, res/xml/file_paths.xml. Here is the content required for this particular example:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>
If you would like more information: read up here
https://developer.android.com/training/camera/photobasics.html
Besides the solution using the FileProvider, there is another way to work around this. Simply put in Application.onCreate() method. In this way the VM ignores the file URI exposure.
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
The reason of this error is that file:// uri scheme no more supported because the security is exposed.
https://code.google.com/p/android/issues/detail?id=203555
And We can not use file:// uri any more after with targetSDK 'N'.
https://commonsware.com/blog/2016/03/14/psa-file-scheme-ban-n-developer-preview.html
So, answer is right. Anyone who use file:// have change content:// to provide kinds of local files.
To sum up :
file:// scheme is now not allowed to be attached with Intent on targetSdkVersion 24 (Android Nougat)
You have to Change your code if you plan to support api 24+
two links :
https://developer.android.com/training/camera/photobasics.html
https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en