Capture and crop image in Android 11 (R)
Asked Answered
B

1

2

As per android R privacy policy changes, I want to perform the capture and crop image feature for android R devices. I tried the below method but it saving the empty file (file is creating in a specified folder but having a size of 0kb).

I'm using Android-image-cropper library to crop images.

public static File createImageFile(Context context) throws IOException {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
    String imageFileName = "JPEG_" + timeStamp + ".jpg";

    File storageDir, image;

    if(SDK_INT >= Build.VERSION_CODES.Q){
        OutputStream outputStream;
        ContentResolver contentResolver = context.getContentResolver();
        ContentValues contentValues = new ContentValues();
        contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, imageFileName);
        contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
        contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES +  File.separator + SD_FOLDER_NAME);
        Uri imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
        outputStream = contentResolver.openOutputStream(Objects.requireNonNull(imageUri));
        Objects.requireNonNull(outputStream);
        storageDir = new File(Environment.DIRECTORY_PICTURES +  File.separator + SD_FOLDER_NAME);
    }else{
        storageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + SD_FOLDER_NAME);
    }

    if (!storageDir.exists()) {
        storageDir.mkdirs();
    }

    image = new File(storageDir, imageFileName);

    if (image.createNewFile()) {
        Log.d(TAG, ":Image file created");
    } else {
        Log.d(TAG, ":Image file not created");
    }
    return image;
}

Below are my camera open and capture functions:

 private void openCamera() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        Uri photoURI = null;
        try {
            fileUri = Utils.createImageFile(requireContext());
            if(SDK_INT >= Build.VERSION_CODES.Q)
                photoURI = FileProvider.getUriForFile(requireActivity(), requireActivity().getPackageName(), fileUri);
            else
                photoURI = FileProvider.getUriForFile(requireActivity(), requireActivity().getPackageName() + ".com.enrich.salonapp.provider", fileUri);
        } catch (IOException e) {
            e.printStackTrace();
        }
        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
        startActivityForResult(intent, REQUEST_CODE_CAMERA_PICTURE);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PictureSelectorDialog.REQUEST_CODE_CAMERA_PICTURE && resultCode == Activity.RESULT_OK) {
            imageCapture.onImageCaptured(fileUri.getAbsolutePath(), false);
        } else if (requestCode == PictureSelectorDialog.REQUEST_CODE_GALLERY_PICTURE && resultCode == Activity.RESULT_OK) {
            if (FileUtils.getPath(getActivity(), data.getData()) == null) {
                imageCapture.onImageCaptured(data.getData().toString(), true);
            } else {
                imageCapture.onImageCaptured(FileUtils.getPath(getActivity(), data.getData()), false);
            }
        }

        this.dismissAllowingStateLoss();
    }

Also, I didn't found how to get URI using file provider.

if(SDK_INT >= Build.VERSION_CODES.Q)
                photoURI = FileProvider.getUriForFile(requireActivity(), requireActivity().getPackageName(), fileUri);
Boff answered 15/6, 2021 at 10:9 Comment(6)
Please mention full paths. Both for old and new.Scrofula
@Scrofula didn't understand. Can you please brief what you want to say?Boff
storageDir = new File(Environment.DIRECTORY_PICTURES + File.separator + SD_FOLDER_NAME); That is nonsense code messing around with the File class as you just obtained a nice uri from .insert(). Use that uri to write the file.Scrofula
storageDir.getAbsolutePath() and image.getAbsolutePath() were the full paths asked.Scrofula
@Scrofula how I can get photoURI from file in Sdk >= Q condition? what to write at the authority place?Boff
You are not reacting in a normal way to my comments nor giving the info i asked for.Scrofula
B
0

After seeing multiple answers, developer docs, and many more. I ended up with this below working solution, Hope this will help someone.

AndroidManifest

 <uses-permission android:name="android.permission.CAMERA" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="29"/>

android:requestLegacyExternalStorage="true"

 <provider
     android:name=".util.GenericFileProvider"
     android:authorities="${applicationId}.provider.profile"
     android:exported="false"
     android:grantUriPermissions="true">
     <meta-data
          android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/provider_paths"/>
 </provider>

PictureSelectorDialog.java

public static final int REQUEST_CODE_CAMERA_PICTURE = 1232;
public static final int REQUEST_CODE_GALLERY_PICTURE = 1233;
private File fileUri;
private ImageCapture imageCapture;
private boolean isDenied = false;

private void openGallery() {
        Intent intent;
        intent = new Intent(Intent.ACTION_PICK,
                android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(Intent.createChooser(intent, getString(R.string.choose_from_gallery)), REQUEST_CODE_GALLERY_PICTURE);
    }

    private void openCamera() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            Uri photoURI = null;
            try {
                fileUri = Utils.createImageFile(requireContext());
                photoURI = FileProvider.getUriForFile(requireContext(), BuildConfig.APPLICATION_ID + ".provider.profile", fileUri);
            } catch (IOException e) {
                e.printStackTrace();
            }
            intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
            startActivityForResult(intent, REQUEST_CODE_CAMERA_PICTURE);
    }



  @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PictureSelectorDialog.REQUEST_CODE_CAMERA_PICTURE && resultCode == Activity.RESULT_OK) {
                imageCapture.onImageCaptured(fileUri.getAbsolutePath(), false);
        } else if (requestCode == PictureSelectorDialog.REQUEST_CODE_GALLERY_PICTURE && resultCode == Activity.RESULT_OK) {
            if (FileUtils.getPath(getActivity(), data.getData()) == null) {
                Uri uri = data.getData();
                ParcelFileDescriptor parcelFileDescriptor;
                try {
                    parcelFileDescriptor = requireContext().getContentResolver().openFileDescriptor(uri, "r");
                    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
                    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
                    parcelFileDescriptor.close();

                    File tempFile = createImageFile(requireContext());

                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    image.compress(Bitmap.CompressFormat.JPEG, 100 , bos);
                    byte[] bitmapData = bos.toByteArray();

                    FileOutputStream fos = new FileOutputStream(tempFile);
                    fos.write(bitmapData);

                    imageCapture.onImageCaptured( tempFile.getAbsolutePath(), true);

                    fos.flush();
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                imageCapture.onImageCaptured(FileUtils.getPath(getActivity(), data.getData()), false);
            }
        }

        this.dismissAllowingStateLoss();
    }

public interface ImageCapture {
        void onImageCaptured(String path, boolean isGooglePhotoURI);
    }

Utils.java

public static File createImageFile(Context context) throws IOException {
        String timeStamp = new SimpleDateFormat(YYYY_MM_DD_HH_MM_SS_FOR_IMAGE_NAMING, Locale.getDefault()).format(new Date());
        String imageFileName = "PROFILE_PICTURE_" + timeStamp;
        File image, storageDir;

        if (SDK_INT < Build.VERSION_CODES.Q) {
            storageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + YOUR_FOLDER_NAME);
            if (!storageDir.exists()) {
                storageDir.mkdirs();
            }
            image = new File(storageDir, imageFileName + ".jpg");
            image.createNewFile();
        } else {
            storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES + File.separator + YOUR_FOLDER_NAME);
            image = File.createTempFile(imageFileName, ".jpg", storageDir);
            String currentPhotoPath = image.getAbsolutePath();
        }

        return image;
    }
Boff answered 9/7, 2021 at 14:27 Comment(1)
The only issue in your approach would be that pictures captured on <Android Q will be stored on device even after the app is uninstalled but for >= Android Q they will be deleted as soon the app is uninstalled, since you are using getExternalFilesDir().Syringa

© 2022 - 2024 — McMap. All rights reserved.