Thursday 20 July 2017

Resolved Nought(7.0) Camera Issue (file:// Schema is not allowed to be attached with intent)

file:// is not allowed(Android N) to attach with Intent anymore or it will
throw FileUriExposedException which may cause your app crash immediately called.

Solution:

So if file:// is not allowed anymore, which approach should we go for? The answer is we should send the URI through content:// scheme instead which is the URI scheme for Content Provider. In this case, we would like to share an access to a file through our app so FileProvider is needed to be implemented.

Step - 1:
It is quite easy to implement FileProvider on your application. First you need to add a FileProvider <provider> tag in AndroidManifest.xml under <application> tag like below:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

Step: 2

And then create a provider_paths.xml file in xml folder under res folder. Folder may be needed to create if it doesn't exist.
The content of the file is shown below. It describes that we would like to share access to the External Storage at root folder (path=".") with the name external_files.
res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
Done! FileProvider is now declared and be ready to use.
Step:3
The final step is to change the line of code below in MainActivity.java
Uri photoURI = Uri.fromFile(createImageFile());
to
Uri photoURI = FileProvider.getUriForFile(MainActivity.this,
        BuildConfig.APPLICATION_ID + ".provider",
        createImageFile());
And .... done ! Your application should now work perfectly fine on any Android version including Android Nougat. Yah !

Why Nougat does not allow passing file:// with Intent anymore?

You may be curious why Android team decide to change this behavior. Actually there is a good reason behind.
If file path is sent to the target application (Camera app in this case), file will be fully accessed through the Camera app's process not the sender one.
But let's consider thoroughly, actually Camera is launched by our application to take a photo and save as a file on our app's behalf. So the access right to that file should be our app's not Camera's. Every operation did with the file should be done through our application not by Camera app itself.
And that's why file:// is now prohibited on targetSdkVersion 24 to force every developer to do this task in the proper way.

2 comments:

  1. What does this method "createImageFile()" do ?

    ReplyDelete
    Replies
    1. More information here: https://stackoverflow.com/questions/7887078/android-saving-file-to-external-storage

      Delete