I am trying to take image from camera with Scoped Directory Access, but it gives me following exception as follows,
java.io.IOException: Permission denied
09-22 22:43:40.556 23767-23767/mp.wall.client W/System.err: at java.io.UnixFileSystem.createFileExclusively0(Native Method)
09-22 22:43:40.556 23767-23767/mp.wall.client W/System.err: at java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:280)
09-22 22:43:40.556 23767-23767/mp.wall.client W/System.err: at java.io.File.createNewFile(File.java:948)
09-22 22:43:40.556 23767-23767/mp.wall.client W/System.err: at java.io.File.createTempFile(File.java:1862)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at mp.wall.utility.imageCapturePickerCompressor.ImageCapture.getCameraPicturesLocation(ImageCapture.java:91)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at mp.wall.utility.imageCapturePickerCompressor.ImageCapture.createCameraPictureFile(ImageCapture.java:64)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at mp.wall.utility.imageCapturePickerCompressor.ImageCapture.createCameraIntent(ImageCapture.java:50)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at mp.wall.utility.imageCapturePickerCompressor.ImageCapture.openCamera(ImageCapture.java:116)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at mp.wall.ui.dashboard.viewInfo.personalInfo.editProfile.EditProfile.onActivityResult(EditProfile.java:219)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at mp.wall.ui.dashboard.viewInfo.personalInfo.editProfile.imagePickerDialog.ImagePickerDialogFragment.sideMenuItem(ImagePickerDialogFragment.java:65)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at mp.wall.ui.dashboard.viewInfo.personalInfo.editProfile.imagePickerDialog.ItemAdapter$VHItem.onClick(ItemAdapter.java:58)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at android.view.View.performClick(View.java:6205)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at android.widget.TextView.performClick(TextView.java:11103)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at android.view.View$PerformClick.run(View.java:23653)
09-22 22:43:40.557 23767-23767/mp.wall.client W/System.err: at android.os.Handler.handleCallback(Handler.java:751)
09-22 22:43:40.558 23767-23767/mp.wall.client W/System.err: at android.os.Handler.dispatchMessage(Handler.java:95)
09-22 22:43:40.558 23767-23767/mp.wall.client W/System.err: at android.os.Looper.loop(Looper.java:154)
09-22 22:43:40.558 23767-23767/mp.wall.client W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6682)
09-22 22:43:40.558 23767-23767/mp.wall.client W/System.err: at java.lang.reflect.Method.invoke(Native Method)
09-22 22:43:40.558 23767-23767/mp.wall.client W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1534)
09-22 22:43:40.558 23767-23767/mp.wall.client W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1424)
as per the documentation of google, it is an alternative of Requesting READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE in your manifest, my code work great below Naugat but since i had removed these permission for Naugat and used Scoped Directory Access but it throws the above mentioned error.
Following is my fragment code,
package mp.wall.ui.dashboard.viewInfo.personalInfo.editProfile;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.Fragment;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.BitmapImageViewTarget;
import java.io.File;
import mp.wall.R;
import mp.wall.controllers.constants.NumericEnums;
import mp.wall.controllers.constants.RestConstants;
import mp.wall.ui.dashboard.viewInfo.personalInfo.editProfile.imagePickerDialog.ImagePickerDialogFragment;
import mp.wall.utility.AndroidVersions;
import mp.wall.utility.ContextFunction;
import mp.wall.utility.IMPermission;
import mp.wall.utility.Intents;
import mp.wall.utility.imageCapturePickerCompressor.ImageCapture;
import mp.wall.utility.imageCapturePickerCompressor.ImageCapturePickerCallbacks;
import tr004.libraryutils.LogUtil;
public class EditProfile extends Fragment implements View.OnClickListener, ImageCapturePickerCallbacks {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.fragment_edit_profile, container, false);
}
@Override
public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ImageView imgBack = ContextFunction.findViewByIdAndCast(view, R.id.hb_img_back);
imgBack.setOnClickListener(this);
TextView tvTitle = ContextFunction.findViewByIdAndCast(view, R.id.hb_tv_title);
tvTitle.setText(ContextFunction.getStringResource(context, R.string.edit_info_profile_title));
tvTitle.setGravity(Gravity.LEFT);
TextView tvSave = ContextFunction.findViewByIdAndCast(view, R.id.hb_tv_action);
tvSave.setText(ContextFunction.getStringResource(context, R.string.edit_info_profile_save));
ContextFunction.viewShow(tvSave);
tvSave.setOnClickListener(this);
ImageView imgCam = ContextFunction.findViewByIdAndCast(view, R.id.fep_img_camGallery);
imgCam.setOnClickListener(this);
Button btChangePassword = ContextFunction.findViewByIdAndCast(view, R.id.fep_bt_changePassword);
btChangePassword.setOnClickListener(this);
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.fep_img_camGallery:
if(AndroidVersions.isNaugatOrHighier())
{
StorageManager sm = (StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
StorageVolume volume = sm.getPrimaryStorageVolume();
Intent intent = volume.createAccessIntent(Environment.DIRECTORY_DCIM);
startActivityForResult(intent, 2001);
}else
openGalleryAndCamera();
break;
}
}
private void openGalleryAndCamera() {
String[] perms = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (IMPermission.hasPermissions(this, perms))
showImagePickerDialogFragment();
else
IMPermission.requestPermissions(this, ContextFunction.getStringResource(context,
R.string.perm_common, R.string.perm_camera_read_write),
NumericEnums.PermissionConstant.CAMERA_GALLERY.getCode(), perms);
}
private void showImagePickerDialogFragment()
{
ImageCapture.openCamera(this, this);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
NumericEnums.PermissionConstant permissionConstant = NumericEnums.PermissionConstant.fromRepresentation(requestCode);
switch (permissionConstant) {
case CAMERA_GALLERY:
if (ContextFunction.checkAllPermisssionGranterd(permissions, grantResults))
openGalleryAndCamera();
break;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(requestCode) {
case 2001:
if (Activity.RESULT_OK == resultCode ) {
showImagePickerDialogFragment();
}else
LogUtil.e("per","permisson not granted");
break;
default:
ImageCapture.handleActivityResult(requestCode,resultCode, data, this, this);
}
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void success(File file) {
Glide.with(context).asBitmap().load(file).into(new BitmapImageViewTarget(imgPic) {
@Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable =
RoundedBitmapDrawableFactory.create(getResources(),
resource);
circularBitmapDrawable.setCircular(true);
imgPic.setImageDrawable(circularBitmapDrawable);
}
});
}
@Override
public void onImagePickerError(Exception e, NumericEnums.DeviceFeaturesCons source) {
ContextFunction.showToast(context, R.string.field_select, R.string.image_selectin_error);
}
@Override
public void onCanceled(NumericEnums.DeviceFeaturesCons source) {
File photoFile = ImageCapture.lastlyTakenButCanceledPhoto(context);
if (photoFile != null)
photoFile.delete();
}
@Override
public void onError(String msg) {
showToast("simple error");
}
}
Following is my IMageCapture file as follows,
package mp.Wall.utility.imageCapturePickerCompressor;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.content.FileProvider;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import mp.Wall.BuildConfig;
import mp.Wall.controllers.constants.NumericEnums;
import mp.Wall.data.sPrefs.StorageManager;
import mp.Wall.utility.ContextFunction;
import mp.Wall.utility.Validation;
public class ImageCapture
{
private static final String KEY_PHOTO_URI = "thGreat004.photopicker.photo_uri";
private static final String KEY_LAST_CAMERA_PHOTO = "thGreat004.photopicker.last_photo";
private static Intent createCameraIntent(@NonNull Object object, ImageCapturePickerCallbacks imageCapturePickerCallback) {
Context context= ContextFunction.getActivity(object);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
try {
Uri capturedImageUri = createCameraPictureFile(context);
//We have to explicitly grant the write permission since Intent.setFlag
// works only on API Level >=20
grantWritePermission(context, intent, capturedImageUri);
intent.putExtra(MediaStore.EXTRA_OUTPUT, capturedImageUri);
} catch (Exception e) {
e.printStackTrace();
imageCapturePickerCallback.onError("errorOccured");
}
return intent;
}
public static Uri createCameraPictureFile(@NonNull Context context) throws IOException {
File imagePath = getCameraPicturesLocation();
Uri uri = getUriToFile(context, imagePath);
StorageManager storageManager = StorageManager.getInstance(context);
storageManager.setValue(KEY_PHOTO_URI, uri.toString());
storageManager.setValue(KEY_LAST_CAMERA_PHOTO, imagePath.toString());
return uri;
}
private static void grantWritePermission(@NonNull Context context, Intent intent, Uri uri) {
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
private static void revokeWritePermission(@NonNull Context context, Uri uri) {
context.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
private static File getCameraPicturesLocation() throws IOException {
File dir = imageDirectory();
return File.createTempFile(UUID.randomUUID().toString(), ".jpg", dir);
}
private static Uri getUriToFile(@NonNull Context context, @NonNull File file) {
String authority = BuildConfig.APPLICATION_ID + ".Wall.fileprovider";
return FileProvider.getUriForFile(context, authority, file);
}
public static void openCamera(Fragment fragment, ImageCapturePickerCallbacks imageCapturePickerCallback) {
Intent intent = createCameraIntent(fragment, imageCapturePickerCallback);
// Ensure that there's a camera activity to handle the intent
if (!Validation.isNull(intent.resolveActivity(fragment.getContext().getPackageManager()))) {
fragment.startActivityForResult(intent, NumericEnums.DeviceFeaturesCons.CAMERA
.getCode());
}else
imageCapturePickerCallback.onError("No app is installed to handle camera");
// ContextFunction.showToast(activity, R.string.intents_no_app_common, R.string.cons_gallery);
}
public static void handleActivityResult(int requestCode, int resultCode, Intent data,
Object object, ImageCapturePickerCallbacks imageCapturePickerCallback) {
{
if (Activity.RESULT_OK == resultCode) {
if (NumericEnums.DeviceFeaturesCons.CAMERA.getCode() == requestCode) {
onPictureReturnedFromCamera(object, imageCapturePickerCallback);
}
}
else if (Activity.RESULT_CANCELED == resultCode){
if (NumericEnums.DeviceFeaturesCons.CAMERA.getCode() == requestCode)
imageCapturePickerCallback.onCanceled(NumericEnums.DeviceFeaturesCons.CAMERA);
}
}
}
private static void onPictureReturnedFromCamera(Object object,ImageCapturePickerCallbacks imageCapturePickerCallback) {
Activity activity= ContextFunction.getActivity(object);
String lastImageUri = StorageManager.getInstance(activity).getValue(KEY_PHOTO_URI, null);
if (!Validation.isEmpty(lastImageUri))
revokeWritePermission(activity, Uri.parse(lastImageUri));
File photoFile = takenCameraPicture(activity);
if (Validation.isNull(photoFile)) {
Exception e = new IllegalStateException("Unable to get the picture " +
"returned from camera");
imageCapturePickerCallback.onImagePickerError(e, NumericEnums.DeviceFeaturesCons.CAMERA);
} else
compressImage(photoFile,imageCapturePickerCallback);
clearPhotoStorate(activity);
}
private static void compressImage(File file,ImageCapturePickerCallbacks imageCapturePickerCallback)
{
imageCapturePickerCallback.success(file);
}
public static void clearPhotoStorate(Context context)
{
StorageManager.getInstance(context).removeValue(KEY_LAST_CAMERA_PHOTO);
StorageManager.getInstance(context).removeValue(KEY_PHOTO_URI);
}
private static File imageDirectory() {
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Wall");
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("sdf", "Oops! Failed create "
+ "Wall" + " directory");
return null;
}
}
return mediaStorageDir;
}
@Nullable
private static File takenCameraPicture(Context context){
String lastCameraPhoto = StorageManager.getInstance(context).getValue(KEY_LAST_CAMERA_PHOTO, null);
if (!Validation.isEmptyorNull(lastCameraPhoto))
return new File(lastCameraPhoto);
else
return null;
}
/**
* @param context context
* @return File containing lastly taken (using camera) photo. Returns null if
* there was no photo taken or it doesn't exist anymore.
*/
public static File lastlyTakenButCanceledPhoto(@NonNull Context context) {
String filePath =StorageManager.getInstance(context).getValue(KEY_LAST_CAMERA_PHOTO, null);
if (filePath == null)
return null;
File file = new File(filePath);
if (file.exists())
return file;
else
return null;
}
}
Here is My manifest file as follows,
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="23"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="23"/>
<application
android:label="Wall"
android:theme="@style/AppTheme">
<activity android:name=".ui.splash.SplashActivity"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.wall.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
and last this is my filepath.xml as follows,
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="WallParkImages" path="DCIM/Wall" />
</paths>