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>
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);