7

What do I want to achieve? I want to get URI of the captured image and save it on Firebase. What did I try? First of all I needed to open the camera. Below how I did it:

 Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    if (cameraIntent.resolveActivity(getActivity().getPackageManager()) != null)
        startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE);

After capturing the image, I needed to get the URI of the image. Below how I did it:

if (resultCode == Activity.RESULT_OK) {
        if (requestCode == CAMERA_REQUEST_CODE) {
            if (data != null) {
                if (data.getData() != null) {
                   Uri capturedImageUri = data.getData(); 
                } else {
                    Toast.makeText(getContext(), getString(R.string.coudldnt_get_photo), Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(getContext(), getString(R.string.coudldnt_get_photo), Toast.LENGTH_SHORT).show();
            }

So everything was OK. But only on some devices. I suppose that it works only on API level more than 21 devices. In other devices , getData() returns null.

So, what I have done next? I found that I can get Bitmap of the image through following code:

Bitmap bitmap = (Bitmap)data.getExtras().get("data") ; 

So, I have a bitmap of the image. And I needed to get the URI of this image. Below how I did it:

public Uri getImageUri(Context inContext, Bitmap inImage) {
  String path = 
  Images.Media.insertImage(inContext.getContentResolver(), inImage, 
  "Title", null);
  return Uri.parse(path);
 } 

Above code returns me URI, but the image has really bad quality.

So, I kept looking for a solution. And found that the right way is to create the file when I start the CameraActivity and save the uri of that file. And onActivityResult I tried to get that uri. Below how I did it:

Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

    if (cameraIntent.resolveActivity(getActivity().getPackageManager()) != null) {
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {

        }
        if (photoFile != null) {
            Uri photoUri = FileProvider.getUriForFile(getContext(), "i_dont_know_what_to_write_here", photoFile);
            cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
            startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE);
        }

    }

And my onActivityResult became:

    if (resultCode == Activity.RESULT_OK) {
    if (requestCode == CAMERA_REQUEST_CODE) {
        if (data != null) {
 Uri capturedPhotoUri = 
 data.getParcelableExtra(MediaStore.EXTRA_OUTPUT);
   } else {
   Toast.makeText(getContext(), 
   getString(R.string.coudldnt_get_photo), Toast.LENGTH_SHORT).show();
            }

The above concept requires to add some tags on manifest :

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

My xml file is :

<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="images"/>
</paths>

The above method didn't work because data on onActivityReuslt was null.

So, what I need to do in order to achieve my goal?

Maddy
  • 4,525
  • 4
  • 33
  • 53
neo
  • 1,314
  • 2
  • 14
  • 34
  • @akhilesh0707 Thanks, but the answer there about how to get filepath from uri. My problem is to get uri. How can I use that? – neo Nov 23 '17 at 05:50
  • `So, I have a bitmap of the image. And I needed to get the URI of this image.`. Yes you have a bitmap. You only have a bitmap. Bitmaps have no uri. So do not ask for an uri of that bitmap/image. What you can do however is save that bitmap to file and then get an uri from the file. Or save the bitmap to the mediastore and then obtain an uri from the store. But still bitmaps have no uri. – greenapps Nov 23 '17 at 07:18

2 Answers2

15

Follow below steps.

Step - 1 : Create provider_paths.xml in res/xml folder and write below code in it.

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="external_files"
        path="." />
</paths>

Step - 2 : Declare provider in manifest as below.

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

If you are facing error at: android:name="android.support.v4.content.FileProvider" Use:

<provider
    android:name="androidx.core.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>

Step - 3 : Write your camera intent as below.

    Intent m_intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    File file = new File(Environment.getExternalStorageDirectory(), "MyPhoto.jpg");
    Uri uri = FileProvider.getUriForFile(this, this.getApplicationContext().getPackageName() + ".provider", file);
    m_intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(m_intent, REQUEST_CAMERA_IMAGE);

Step - 4 : Handle camera result in onActivityResult as below.

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {

        //TODO... onCamera Picker Result
        case REQUEST_CAMERA_IMAGE:
            if (resultCode == RESULT_OK) {

              //File object of camera image
                File file = new File(Environment.getExternalStorageDirectory(), "MyPhoto.jpg");

              //Uri of camera image
                Uri uri = FileProvider.getUriForFile(this, this.getApplicationContext().getPackageName() + ".provider", file);

            }
            break;
    }
}
Pawan
  • 533
  • 1
  • 7
  • 31
  • Your solution requires more code. I didn't try it yet. But want to ask, what is the advantages of your solution over below answer? – neo Nov 23 '17 at 06:57
  • I have used FileProvider concept, So this code will work fine in all android versions ( KitKat to Oreo ). Moreover, this is google recommendation to use FileProvider if you want to access file directly. Check out this link https://developer.android.com/reference/android/support/v4/content/FileProvider.html – Kumarpalsinh Rana Nov 23 '17 at 07:05
  • Very clean solution! I got URI successfully, after that I need to show it on recyclerview. But Picasso couldn't load that image through its URI. What can be the problem? – neo Nov 24 '17 at 04:37
  • Well, I have used URL to load an image using glide and it works fine. But if you failed to load an image using URI then you can also try file.getAbsolutePath() to get captured image path. – Kumarpalsinh Rana Nov 24 '17 at 06:48
  • I will accept your answer, but can you help me with these problems: 1) As I've noticed , the photos saved in device storage. But it has no folder, it is putted among folders. How can I create the folder for photo? 2) Do I need to ask `WRITE_EXTERNAL_STORAGE` permission for your method? 3) `file.getAbsolutePath()` returns following: `/storage/emulated/0/MyPhoto.jpg` , when `getUriForFile()` returns : `content://app_package_name.provider/external_files/MyPhoto.jpg` Why they are different? 4)Picasso and Glide both couldn't load photo by uri converted to string in adapter. How to solve? – neo Nov 24 '17 at 08:27
  • I don't think that there is any problem in Glide or Image path. Because Glide works with both paths i.e. URI Path as well as File Path. I think you are facing issue because of storage permissions. Just give two storage permissions i.e. WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE and check it. Hope this will work for you. – Kumarpalsinh Rana Nov 24 '17 at 09:22
  • @Kumarpalsinh Rana, Pawan getExternalStorageDirectory() is deprecated. I am struggling to find my way forward with this. Could update for api 31 please. – Peter May 16 '22 at 13:00
6

While calling Camera intent

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);


 File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "IMG_FOLDER");

        try {
            if (!mediaStorageDir.exists()) {
                if (!mediaStorageDir.mkdirs()) {
                    return null;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        imageURI = Uri.fromFile(new File(mediaStorageDir.getPath() + File.separator +
                "profile_img.jpg"));
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageURI);

        startActivityForResult(intent, Utils.CAMERA_REQUEST);

Inside onActivityResult

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        try {
            if (resultCode == RESULT_OK) {
                if (requestCode == Utils.CAMERA_REQUEST) {
                    if (imageURI != null) {             // use the same uri, that you initialized while calling camera intent
                     // do whatever you want to do with this Uri
                 }
                }
            }
         } catch(Exception E) {}  
  }

For Nougat, you need to add this below code in splash / launcher activity

if (Build.VERSION.SDK_INT >= 24) {
        try {
            Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
            m.invoke(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

This will work in all version. I have tested it too.

Akshay Barodia
  • 486
  • 4
  • 11
  • Your solution is good, but it overwrites the previous captured image. So, how to avoid this? – neo Nov 23 '17 at 06:51
  • you can use arrayList for adding new images. – Akshay Barodia Nov 23 '17 at 06:58
  • android.os.FileUriExposedException: file:///storage/emulated/0/Pictures/Qamqor/qamqor_20171124_120531.jpg exposed beyond app through ClipData.Item.getUri() Unfortunately, I got above error when cameraActivity starts – neo Nov 24 '17 at 06:07
  • My target API was 13 , its pretty old and when I tried to install the application in Android 10 and 11 , the camera capture is giving problems because we can't access the cache folder. I can't use FileProvider as the target API was 13. When I tried upgrading the target API to latest one, I need to do lot of code changes. I need to manage by not using FileProvider but at the same time I need to store the image in external storage. Your code helped me. Thanks for that. – Baskar PC Apr 07 '22 at 08:47