javaandroidandroid-camera-intentmediastorefile-uri

Unable to get File Uri for PutExtra method for camera


I am trying to build a camera activity that takes a photo and saves it to the SD card. But I found that my code is breaking at the method used to obtain the File Uri as well as the putExtra() method.

When the startCamera() function is called, the camera intent does not launch and kills the activity.

private void startCamera(int actionCode) {
    //Create a camera intent
    Intent camera_intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    switch (actionCode) {
        case CAM_REQ_CODE:
        File photo_file = null;
        try {
            photo_file = setUpPhotoFile();
        } catch (IOException e) {
            e.printStackTrace();
            photo_file = null;
            photo_path = null;
        }
        // Continue only if the File was successfully created
        if (photo_file != null) {
            photo_path = photo_file.getAbsolutePath();
            Uri photoURI = Uri.fromFile(photo_file);  //works
            //Uri photoURI = FileProvider.getUriForFile(InStore.this,"babygroot.iamgroot.fileprovider", photo_file);
            // PROBLEM!! how to overcome putExtra method to pass photo URI to MediaStore.EXTRA_OUTPUT
            camera_intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
            startActivityForResult(camera_intent, CAM_REQ_CODE);
        }
        break;
        default:
            break;
    }
}

And this is my manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="babygroot.iamgroot">

    <!-- To auto-complete the email text field in the login form with the user's emails -->
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.READ_PROFILE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <uses-feature android:name="android.hardware.camera" />
    <!--<uses-feature android:name="android.hardware.camera2" /> -->
    <uses-feature android:name="android.hardware.camera.autofocus" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="babygroot.iamgroot.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"></meta-data>
        </provider>

        <activity android:name=".LoginScreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".QRCode"
            android:parentActivityName=".LoginScreen" />
        <activity
            android:name=".SignUp"
            android:parentActivityName=".LoginScreen" />
        <activity
            android:name=".InStore"
            android:parentActivityName=".QRCode" />
        <activity android:name=".CheckOut" />
    </application>

</manifest>

The InStore activity is where the camera function is used.

This is the stacktrace when it fails

Process: babygroot.iamgroot, PID: 26238
    java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Pictures/IAMGROOTPics/IMG_20171213_141445_787397456.jpg
                  at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:712)
                  at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:401)
                  at babygroot.iamgroot.InStore.startCamera(InStore.java:172)
                  at babygroot.iamgroot.InStore.showCameraPreview(InStore.java:111)
                  at babygroot.iamgroot.InStore.access$000(InStore.java:28)
                  at babygroot.iamgroot.InStore$1.onClick(InStore.java:63)
                  at android.view.View.performClick(View.java:5646)
                  at android.view.View$PerformClick.run(View.java:22459)
                  at android.os.Handler.handleCallback(Handler.java:761)
                  at android.os.Handler.dispatchMessage(Handler.java:98)
                  at android.os.Looper.loop(Looper.java:156)
                  at android.app.ActivityThread.main(ActivityThread.java:6523)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)

res/xml/file_paths.xml:

<?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.babygroot.iamgroot/files/Pictures" />
</paths>

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.example.package.babygroot.iamgroot/files/Pictures" />
</paths>

Solution

  • The issue is android now does not allow files read of other applications so thats why your code is crashing. See this 7.0 behaviour changes

    See this tutorial to do your task using FileProvider https://drivy.engineering/android-fileprovider/

    You basically declare a shared file location so that apps can do read and write there. Then create a file there and use the Uri of that file to open camera intent.

    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <files-path name="shared" path="shared/"/>
    </paths>
    

    Menfiest File declaration

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="<your provider authority>"
        android:exported="false"
        android:grantUriPermissions="true">
      <meta-data
          android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/file_provider_paths"/>
    </provider>
    

    Finally

    Uri sharedFileUri = FileProvider.getUriForFile(this, <your provider auhtority>, sharedFile);