How to get camera result as a uri in data folder?
Asked Answered
E

6

67

I am creating an application in which I want to capture a image and then I want to send that image in the email as a attachment.

I am opening a camera using android.provider.MediaStore.ACTION_IMAGE_CAPTURE intent action and I am passing the Uri of the file as a parameter EXTRA_OUTPUT to get the image back to the file. This is working perfectly and I am able to get the captured image if I use the external storage uri as a EXTRA_OUTPUT but if I use the data folder uri it is not working and the camera is not closing and its all buttons are not working.

Here is my code for get the result in the external storage directory

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = Environment.getExternalStorageDirectory();
out = new File(out, imagename);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);

And this code is for get the image in the data folder

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = getFilesDir();
out = new File(out, MyPharmacyOptions.PRESCRIPTION_IMAGE_NAME);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);

I knew that the data folder is not accessible to third application so may be this causes an issue so I have create one content provider to share the file.

Here is my content provide class

public class MyContentProvider extends ContentProvider {
    private static final String Tag = RingtonContentProvider.class.getName();
    public static final Uri CONTENT_URI = Uri
            .parse("content://x.y.z/");
    private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>();

    static {
        MIME_TYPES.put(".mp3", "audio/mp3");
        MIME_TYPES.put(".wav", "audio/mp3");
        MIME_TYPES.put(".jpg", "image/jpeg");
    }

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public String getType(Uri uri) {
        String path = uri.toString();

        for (String extension : MIME_TYPES.keySet()) {
            if (path.endsWith(extension)) {
                return (MIME_TYPES.get(extension));
            }
        }

        return (null);
    }

    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode)
            throws FileNotFoundException {
        File f = new File(getContext().getFilesDir(), uri.getPath());

        if (f.exists()) {
            return (ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY));
        }

        throw new FileNotFoundException(uri.getPath());
    }

    @Override
    public Cursor query(Uri url, String[] projection, String selection,
            String[] selectionArgs, String sort) {
        throw new RuntimeException("Operation not supported");
    }

    @Override
    public Uri insert(Uri uri, ContentValues initialValues) {
        File file = new File(getContext().getFilesDir(), uri.getPath());
        if(file.exists()) file.delete();
        try {
            file.createNewFile();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return Uri.fromFile(file);
    }

    @Override
    public int update(Uri uri, ContentValues values, String where,
            String[] whereArgs) {
        throw new RuntimeException("Operation not supported");
    }

    @Override
    public int delete(Uri uri, String where, String[] whereArgs) {
        File f = new File(getContext().getFilesDir(), "image1.jpg");
        if(f.exists()) f.delete();
        f = new File(getContext().getFilesDir(), "image2.jpg");
        if(f.exists()) f.delete();

        getContext().getContentResolver().notifyChange(CONTENT_URI, null);

    }
}

So to use this content provide I am using following code to pass the uri to the camera activity

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
Uri uri = MyContentProvider.CONTENT_URI;
uri = Uri.withAppendedPath(uri, imagename);
getContentResolver().insert(uri, null);
getContentResolver().notifyChange(RingtonContentProvider.CONTENT_URI, null);
Log.d(Tag, uri.toString());
i.putExtra(MediaStore.EXTRA_OUTPUT, uri);

startActivityForResult(i, CAMERA_RESULT);

Now if I pass the url other then external storage directory the camera is opening but it is not closing in emulator but in device the camera is going to closed but I am not getting the result.

I have declared this content provide in the manifest file

<provider
android:name=".contentproviders.MyContentProvider"
android:authorities="x.y.z" />

Also I have given the permission to write the external storage and also for use the camera.

I am able to capture the image using the external storage but I want to store the image in the data directory instead of external storage because if the external storage in not available I want to capture the image and want to send mail.

If I create content provide then I can also share my image to the email application.

If we not provide the extras with the camera intent it will return the image as a byte[] in the activity result as a data extra but this is for the purpose of the thumbnail so I can't get the high resolution image using this way.

Exert answered 6/4, 2012 at 11:15 Comment(1)
What is the return value for public int delete(Uri uri, String where, String[] whereArgs)Laurilaurianne
E
71

There are two ways to solve this problem.

1. Save bitmap which you received from onActivityResult method

You can start camera through intent to capture photo using below code

Intent cameraIntent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);

After capture photo, you will get bitmap in onActivityResult method

if (requestCode == CAMERA_REQUEST) {  
    Bitmap photo = (Bitmap) data.getExtras().get("data"); 
 }

Now you can simply save this bitmap to internal storage

Note: Here bitmap object consists of thumb image, it will not have a full resolution image

2. Save bitmap directly to internal storage using content provider

Here we will create content provider class to allow permission of local storage directory to camera activity

Sample provider example as per below

public class MyFileContentProvider extends ContentProvider {
    public static final Uri CONTENT_URI = Uri.parse
                                    ("content://com.example.camerademo/");
    private static final HashMap<String, String> MIME_TYPES = 
                                     new HashMap<String, String>();

    static {
        MIME_TYPES.put(".jpg", "image/jpeg");
        MIME_TYPES.put(".jpeg", "image/jpeg");
    }

    @Override
    public boolean onCreate() {

        try {
            File mFile = new File(getContext().getFilesDir(), "newImage.jpg");
            if(!mFile.exists()) {
                mFile.createNewFile();
            }
            getContext().getContentResolver().notifyChange(CONTENT_URI, null);
            return (true);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    @Override
    public String getType(Uri uri) {
        String path = uri.toString();

        for (String extension : MIME_TYPES.keySet()) {
            if (path.endsWith(extension)) {
                return (MIME_TYPES.get(extension));
            }
        }
        return (null);
    }

    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode)
    throws FileNotFoundException {

        File f = new File(getContext().getFilesDir(), "newImage.jpg");
        if (f.exists()) {
            return (ParcelFileDescriptor.open(f,
                    ParcelFileDescriptor.MODE_READ_WRITE));
        }
        throw new FileNotFoundException(uri.getPath());
    }
}

After that you can simply use the URI to pass to camera activity using the below code

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAMERA_RESULT);

If you don't want to create your own provider then you can use FileProvider from support-library-v4. For detailed help you can look into this post

Exert answered 19/4, 2012 at 13:34 Comment(11)
I like the simplicity of the first solution and have implemented it in my application. However, it only gets a low res version of the image...Zoster
@DiscoS2 It will give you low resolution image. to get the actual size of the image create one file in your external storage and then pass the file uri with the intent of the camera so when the capture is completed the camera will write the image in your file with the original resolution.Exert
@Exert i'm trying to get actual size image and getting the null intent on onActivityResult(); Here is my code : final String timeString = new SimpleDateFormat("HH_mm_ss_SSS").format(cal.getTime()); image = new File(imagesFolder, "ec_" + timeString + ".jpg"); Uri uriSavedImage = Uri.fromFile(image); intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, uriSavedImage); startActivityForResult(intent, CAMERA_RESULT); Any idea why i'm getting null value of intent?Slurry
@Deadlock You can check my Last Edit in the answer.Exert
but i'm saving the image with the system time stamp(if u check my code). Is it possible to check the image saved which is being saved via a dynamic naming mechanism?Slurry
copy file name in global variable and then use that variable in onActivityResult() methodExert
I don't understand why the Intent would be sent to onActivityResult as null if you're passing in the URI to which the camera should save the photo. It doesn't make sense. The Intent still exists and should be passed just the same.Destiny
Which intent you are telling about?Exert
@Exert Did you try your solution 2, or just missed something when copied somewhere else?Interfluent
@Interfluent What do you mean by copied somewhere else? I have written this code in one of my app and it is working fine. You can check the blog link where I have written detailed description about the solution.Exert
Sorry. Your code is fantastic. But the copy here is not complete. First I tried it and failed, and realized many methods were not implemented. Then I follow the blog, and it works! Thanks. But please pay attention not to give an incomplete and not say so solution. I meet such similar issues many times.Interfluent
A
21

Best solution I found is: FileProvider (needs support-library-v4) It uses the internal storage! https://developer.android.com/reference/android/support/v4/content/FileProvider.html

  1. Define your FileProvider in Manifest in Application element:

    <provider
          android:name="android.support.v4.content.FileProvider"
          android:authorities="your.package.name.fileprovider"
          android:exported="false"
          android:grantUriPermissions="true" >
          <meta-data
                     android:name="android.support.FILE_PROVIDER_PATHS"
                     android:resource="@xml/image_path" />
    </provider>
    
  2. Add permissions in manifest root element:

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    
  3. Define your image paths in for example res/xml/image_path.xml:

    <paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="captured_image" path="your/path/"/>
    </paths>
    
  4. Java:

    private static final int IMAGE_REQUEST_CODE = 1;
    // your authority, must be the same as in your manifest file 
    private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";
    

4.1 capture intent:

    File path = new File(activity.getFilesDir(), "your/path");
    if (!path.exists()) path.mkdirs();
    File image = new File(path, "image.jpg");
    Uri imageUri = FileProvider.getUriForFile(activity, CAPTURE_IMAGE_FILE_PROVIDER, image);
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intent, IMAGE_REQUEST_CODE);

4.2 onActivityResult():

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (requestCode == IMAGE_REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK) {
                File path = new File(getFilesDir(), "your/path");
                if (!path.exists()) path.mkdirs();
                File imageFile = new File(path, "image.jpg");
                // use imageFile to open your image
            }
        }
        super.onActivityResult(requestCode, resultCode, intent);
    }
Arsenate answered 1/12, 2014 at 18:35 Comment(4)
And who is CAPTURE_IMAGE_PROVIDER?Tilbury
// your authority, must be the same as in your manifest file private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";Arsenate
@Arsenate android:authorities="your.package.name.fileprovider" What kind of path it is, can u explain about fileprovider little bit ?Sparkle
Instead of using Uri imageUri = FileProvider.getUriForFile(activity, CAPTURE_IMAGE_FILE_PROVIDER, image); just use Uri imageUri = FileProvider.getUriForFile(activity, getApplicationContext().getPackageName() + ". fileprovider, image);"Eldwin
R
5

Intent to call to capture photo,

Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);  
startActivityForResult(cameraIntent, CAMERA_REQUEST);  

Then Take Bitmap in ActivityResult

 if (requestCode == CAMERA_REQUEST) {   
            Bitmap photo = (Bitmap) data.getExtras().get("data");  
 }   

Then Write that Into Internal Memory, see this

// The openfileOutput() method creates a file on the phone/internal storage in the context of your application  
final FileOutputStream fos = openFileOutput("my_new_image.jpg", Context.MODE_PRIVATE); 

// Use the compress method on the BitMap object to write image to the OutputStream 
bm.compress(CompressFormat.JPEG, 90, fos); 

Then next time to read that file,

Bitmap bitmap = BitmapFactory.decodeFile(file); 
Rabato answered 18/4, 2012 at 12:51 Comment(1)
It is good but by this way i am getting thumbnail image i want to get the original image how??Marciemarcile
G
3

At first save came photo in your external storage and try it -

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.main);
   this.imageView = (ImageView)this.findViewById(R.id.imageView1);
   Button photoButton = (Button) this.findViewById(R.id.button1);
   photoButton.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); 
        startActivityForResult(cameraIntent, CAMERA_REQUEST); 
    }
});
}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
   if (requestCode == CAMERA_REQUEST) {  
        Bitmap bmp = intent.getExtras().get("data");
        ByteArrayOutputStream stream = new ByteArrayOutputStream();

         bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
         byte[] byteArray = stream.toByteArray(); // convert camera photo to byte array

         // save it in your external storage.
        FileOutputStream fo = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/_camera.png"));

        fo.write(byteArray);
        fo.flush();
        fo.close();
   }  
} 

Next target -

File cameraFile = new File(Environment.getExternalStorageDirectory() + "/_camera.png");                 
startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_SEND)
        .setType("image/jpg")
        .putExtra(Intent.EXTRA_SUBJECT, "Subject")
        .putExtra(Intent.EXTRA_STREAM, Uri.fromFile(cameraFile))
        .putExtra(Intent.EXTRA_TEXT, textBody), "Send your message using"), Constant.EMAIL);
Guileful answered 6/4, 2012 at 11:35 Comment(13)
I am able to capture the image using the external storage but I wan't to store the image in the data directory instead of external storage because if the external storage in not available I want to capture the image and want to send mail. If I create content provide then I can also share my image to the email application.Exert
save the image using this path - </data/data/<your package name>/camera.png>Guileful
Thanks for your quick reply.This is the one possible way to save the image in the data folder but this image is for the thumbnail purpose so quality of this image is very less and so I can't use this image.Exert
I have to use the image for the prescription purpose and so I can't use this thumbnail imageExert
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream); this is maintaing for quality. So, you can modify it. Use 50 or 40 instead of 100% compression.Guileful
I have used that same way as you are telling but the original resolution and quality is so week so we can't increase the quality. +1 for your suggestions.thanksExert
try it x.compress(Bitmap.CompressFormat.JPEG, 70, outstream);Guileful
Or you can also try - bmp.compress(Bitmap.CompressFormat.PNG, 0, stream);Guileful
I had try with bmp.compress(Bitmap.CompressFormat.JPEG, 100, outstream); but because of the resolution issue I had dropped this idea.Exert
Using this way if you change the resolution of the camera the result is not affecting. it will remains same for any setting of the camera.Exert
Try it - cameraIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, [</data/data/<your package name>/camera.png>]); startActivity(cameraIntent);Guileful
I tried but getting error Attempt to cast generated internal exception: may be because we are passing the uri as a stringExert
let us continue this discussion in chatExert
S
1

You can also do this without needing a content provider since you will need the sd card to open the camera image capture intent anyway. You can of course hack around the presence of the sd card but not with the camera intent capture....So you are checking for external storage but need it at this point.... FYI you should also check out a crop library like crop-image instead of using native, as it is not well supported across devices.

File mediaStorageDir;
String photoFileName = "photo.jpg";


    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // page = getArguments().getInt("someInt", 0);
    // title = getArguments().getString("someTitle");
    // Get safe storage directory for photos
        mediaStorageDir = new File(
                Environment                          .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                APP_TAG);
        // Create the storage directory if it does not exist
        if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()) {
            Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.isDirectory());
            Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.getPath());
            Log.d(APP_TAG,
                    "Directory exists: "
                            + Environment.getExternalStorageState());
            Log.d(APP_TAG, "failed to create directory");
        }

}

in your 'take the pic' code:

            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri(photoFileName));

...

public Uri getPhotoFileUri(String fileName) {

    return Uri.fromFile(new File(mediaStorageDir.getPath() + File.separator
            + fileName));
}
Salience answered 19/10, 2014 at 3:18 Comment(0)
W
1

After researching this bug for some time, I have noticed that the activity that called the camera intent is only restarted when the phone is running out of memory. so because the activity is restarted, the object holding the path or Uri to the captured image is refreshed (nulled)

so I would suggest you catch/ detect the null object in the onActivityResult, and prompt the user to free up space on their device or restart their phone for a quick temporary fix.

Woolridge answered 14/10, 2015 at 16:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.