when take photo get - java.lang.Throwable: file:// Uri exposed through ClipData.Item.getUri()
Asked Answered
K

5

24

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...

Khartoum answered 17/9, 2015 at 5:45 Comment(0)
K
15

I have already resolved this problem.

First, this problem occurred because StrictMode prevents passing URIs with a file:// scheme.

So there are two solutions:

  1. Change StrictMode. See similar problem and its code. But for our apps, it is not realistic to modify the Android source code.

  2. 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.

Khartoum answered 22/9, 2015 at 6:38 Comment(1)
"...requires android.permission.WRITE_EXTERNAL_STORAGE, or grantUriPermission()"Palmira
G
12

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

Gemmulation answered 15/9, 2016 at 10:32 Comment(0)
E
5

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());
Evadnee answered 2/9, 2017 at 10:47 Comment(2)
Can you please explain what exactly this does?Epistaxis
I don't know what its doing but resolved my issue. ThanksRadiothermy
D
4

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.

Demonstrable answered 13/6, 2016 at 13:14 Comment(1)
Thanks, I downgraded targetSdk to 23 and it has started working. I mean File.getUriFrom(File file)Talbott
T
3

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

Turner answered 3/1, 2017 at 10:15 Comment(1)
Good One,it really helpfulRendering

© 2022 - 2024 — McMap. All rights reserved.