Android ACTION_IMAGE_CAPTURE Intent

186,808

Solution 1

this is a well documented bug in some versions of android. that is, on google experience builds of android, image capture doesn't work as documented. what i've generally used is something like this in a utilities class.

public boolean hasImageCaptureBug() {

    // list of known devices that have the bug
    ArrayList<String> devices = new ArrayList<String>();
    devices.add("android-devphone1/dream_devphone/dream");
    devices.add("generic/sdk/generic");
    devices.add("vodafone/vfpioneer/sapphire");
    devices.add("tmobile/kila/dream");
    devices.add("verizon/voles/sholes");
    devices.add("google_ion/google_ion/sapphire");

    return devices.contains(android.os.Build.BRAND + "/" + android.os.Build.PRODUCT + "/"
            + android.os.Build.DEVICE);

}

then when i launch image capture, i create an intent that checks for the bug.

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
if (hasImageCaptureBug()) {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("/sdcard/tmp")));
} else {
    i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(i, mRequestCode);

then in activity that i return to, i do different things based on the device.

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
     switch (requestCode) {
         case GlobalConstants.IMAGE_CAPTURE:
             Uri u;
             if (hasImageCaptureBug()) {
                 File fi = new File("/sdcard/tmp");
                 try {
                     u = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(), fi.getAbsolutePath(), null, null));
                     if (!fi.delete()) {
                         Log.i("logMarker", "Failed to delete " + fi);
                     }
                 } catch (FileNotFoundException e) {
                     e.printStackTrace();
                 }
             } else {
                u = intent.getData();
            }
    }

this saves you having to write a new camera app, but this code isn't great either. the big problems are

  1. you never get full sized images from the devices with the bug. you get pictures that are 512px wide that are inserted into the image content provider. on devices without the bug, everything works as document, you get a big normal picture.

  2. you have to maintain the list. as written, it is possible for devices to be flashed with a version of android (say cyanogenmod's builds) that has the bug fixed. if that happens, your code will crash. the fix is to use the entire device fingerprint.

Solution 2

I know this has been answered before but I know a lot of people get tripped up on this, so I'm going to add a comment.

I had this exact same problem happen on my Nexus One. This was from the file not existing on the disk before the camera app started. Therefore, I made sure that the file existing before started the camera app. Here's some sample code that I used:

String storageState = Environment.getExternalStorageState();
        if(storageState.equals(Environment.MEDIA_MOUNTED)) {

            String path = Environment.getExternalStorageDirectory().getName() + File.separatorChar + "Android/data/" + MainActivity.this.getPackageName() + "/files/" + md5(upc) + ".jpg";
            _photoFile = new File(path);
            try {
                if(_photoFile.exists() == false) {
                    _photoFile.getParentFile().mkdirs();
                    _photoFile.createNewFile();
                }

            } catch (IOException e) {
                Log.e(TAG, "Could not create file.", e);
            }
            Log.i(TAG, path);

            _fileUri = Uri.fromFile(_photoFile);
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE );
            intent.putExtra( MediaStore.EXTRA_OUTPUT, _fileUri);
            startActivityForResult(intent, TAKE_PICTURE);
        }   else {
            new AlertDialog.Builder(MainActivity.this)
            .setMessage("External Storeage (SD Card) is required.\n\nCurrent state: " + storageState)
            .setCancelable(true).create().show();
        }

I first create a unique (somewhat) file name using an MD5 hash and put it into the appropriate folder. I then check to see if it exists (shouldn't, but its good practice to check anyway). If it does not exist, I get the parent dir (a folder) and create the folder hierarchy up to it (therefore if the folders leading up to the location of the file don't exist, they will after this line. Then after that I create the file. Once the file is created I get the Uri and pass it to the intent and then the OK button works as expected and all is golden.

Now,when the Ok button is pressed on the camera app, the file will be present in the given location. In this example it would be /sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg

There is no need to copy the file in the "onActivityResult" as posted above.

Solution 3

I've been through a number of photo capture strategies, and there always seems to be a case, a platform or certain devices, where some or all of the above strategies will fail in unexpected ways. I was able to find a strategy that uses the URI generation code below which seems to work in most if not all cases.

mPhotoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
            new ContentValues());
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoUri);
startActivityForResult(intent,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE_CONTENT_RESOLVER);

To contribute further to the discussion and help out newcomers I've created a sample/test app that shows several different strategies for photo capture implementation. Contributions of other implementations are definitely encouraged to add to the discussion.

https://github.com/deepwinter/AndroidCameraTester

Solution 4

I had the same problem where the OK button in camera app did nothing, both on emulator and on nexus one.

The problem went away after specifying a safe filename that is without white spaces, without special characters, in MediaStore.EXTRA_OUTPUT Also, if you are specifying a file that resides in a directory that has not yet been created, you have to create it first. Camera app doesn't do mkdir for you.

Solution 5

The workflow you describe should work as you've described it. It might help if you could show us the code around the creation of the Intent. In general, the following pattern should let you do what you're trying.

private void saveFullImage() {
  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
  outputFileUri = Uri.fromFile(file);
  intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
  startActivityForResult(intent, TAKE_PICTURE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if ((requestCode == TAKE_PICTURE) && (resultCode == Activity.RESULT_OK)) {
    // Check if the result includes a thumbnail Bitmap
    if (data == null) {    
      // TODO Do something with the full image stored
      // in outputFileUri. Perhaps copying it to the app folder
    }
  }
}

Note that it is the Camera Activity that will be creating and saving the file, and it's not actually part of your application, so it won't have write permission to your application folder. To save a file to your app folder, create a temporary file on the SD card and move it to your app folder in the onActivityResult handler.

Share:
186,808

Related videos on Youtube

Drew
Author by

Drew

Updated on December 26, 2020

Comments

  • Drew
    Drew over 3 years

    We are trying to use the native camera app to let the user take a new picture. It works just fine if we leave out the EXTRA_OUTPUT extra and returns the small Bitmap image. However, if we putExtra(EXTRA_OUTPUT,...) on the intent before starting it, everything works until you try to hit the "Ok" button in the camera app. The "Ok" button just does nothing. The camera app stays open and nothing locks up. We can cancel out of it, but the file never gets written. What exactly do we have to do to get ACTION_IMAGE_CAPTURE to write the picture taken to a file?

    Edit: This is done via the MediaStore.ACTION_IMAGE_CAPTURE intent, just to be clear

  • Drew
    Drew over 14 years
    This /should/ work, but the camera app's ok button just does nothing. We did get it working writing to the SD card, so I suspect this is a file permissions problem (though I thought that an application automatically had write permissions to it's personal directory). thanks for the help!
  • Reto Meier
    Reto Meier over 14 years
    Your app has write permission to it's personal directory -- the camera app (which is taking the picture) does not. You could move the file in the onActivityResult though, as that's within your app. Alternatively you could implement a camera Activity yourself, but that seems overkill.
  • Drew
    Drew over 14 years
    Redoing the camera app seems to be a common but tedious approach... When we use getDir("images",MODE_WORLD_WRITEABLE), does that permission not apply to any file we tell it to create in that directory?
  • Reto Meier
    Reto Meier over 14 years
    Not necessarily. The permission is for the folder, not necessarily for the files within it.
  • Matthias
    Matthias about 13 years
    where does the "_data" column come from? is there no constant for it?
  • Matthias
    Matthias about 13 years
    ah, it's developer.android.com/reference/android/provider/… You really shouldn't use magic strings in code.
  • jwadsack
    jwadsack about 13 years
    AndroidRuntime (2.1 emulator) throws NoSuchMethodError: java.io.File.setWritable
  • Arseniy
    Arseniy almost 13 years
    on Galaxy S: intent.getData() returns null, and moreover, camera app on Galaxy s inserts new photo into gallery by itself, but no uri returned. So if you insert photo from file into gallery, there will be duplicated photo.
  • Geetanjali
    Geetanjali almost 13 years
    hey my device is not listed here and i have implemented by ur way.but my application still crashes i don knw the reason.please help me
  • swanson
    swanson over 12 years
    Make sure you have <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> in your manifest if you are using newer than Android 1.5 -- spent a few hours debugging the camera stuff and this wasn't included so my saves would always fail.
  • rOrlig
    rOrlig over 12 years
    using this code on droid-x and sony xpheria devices. Both devices return intent value as null. also droidx returns resultcode as 0 whereas xpheria returns resultcode as -1. Anyone know why that should be the case.
  • joey_g216
    joey_g216 over 12 years
    This works good for me, but every about 10 images or so, the blank file is not overwritten and I end up with a 0 byte file where the image should be. It's very annoying and has been tough to track down.
  • FM2020
    FM2020 about 12 years
    The link to the "well documented bug" which is at the beginning of yanokwa's answer is incorrect. That bug is about calling the camera app without putExtra(EXTRA_OUTPUT,...)
  • Keith
    Keith almost 12 years
    on some devices, such as the nexus 7 with jelly bean, you need to change Environment.getExternalStorageDirectory().getName() to use .getAbsolutePath() instead of .getName()
  • Mohammad Ersan
    Mohammad Ersan over 11 years
    your link is not working anymore!! why, i saw it 2 weeks ago, and i wanted to use it :(
  • SHRISH M
    SHRISH M about 11 years
    I tested the code with Android devices with Android 2.2 and newer and all of them saved the image to the supplied path. So I guess that this should be standard behaviour to get images.
  • Pencilcheck
    Pencilcheck about 11 years
    code.google.com/p/android/issues/detail?id=1480 Well, how do you explain this then?
  • Rose Perrone
    Rose Perrone almost 11 years
    Has this bug been fixed yet?
  • Diana
    Diana over 10 years
    And make sure you use mkdirs() instead of mkdir() if your path has more than one directory level and you want all of them created (mkdir only creates the last one, the 'leaf' directory). I had some problems with that, and this answer helped me.
  • Rakeeb Rajbhandari
    Rakeeb Rajbhandari over 10 years
    can we use simple timestamps ?? or can the also make some errors ?? pleas do tag me when replied :)
  • Yenchi
    Yenchi over 10 years
    @user2247689 I am not sure about the file name you created by "simple timestamps" but as long as they don't contain special characters not allowed by FAT format on SD card it should be ok I think.
  • Rich
    Rich over 10 years
    This was great. I've yet to test this across all my devices, but I'm assuming you have done so quite a bit already. There's so much noise on this topic, and this particular answer is so simple and clean. Works on Nexus 5 so far, which is where I first got a lot of reports that ACTION_IMAGE_CAPTURE wasn't working without EXTRA_OUTPUT. I do hope this works all across the board, but so far so good. Thx
  • Billy Coover
    Billy Coover over 10 years
    @deepwinter - Thank you sir. I was pulling my hair out but your content resolver solution works well on all the problematic scenarios I had.
  • StackJP
    StackJP over 9 years
    Kind of late to this party but this has been the only solution to work for me. Thanks a bunch!
  • G_V
    G_V over 9 years
    I have this right now, so it doesn't appear to be but WHO CAN TELL because this camera stuff has so little exposed while having so much complexity. I just want a Camera.OnPictureSnapListener that gives a bitmap, not all this intent generation, bugged bundles, camera not taking pictures crap. Their own code examples don't even work for this, it's ridiculous.
  • Standaa - Remember Monica
    Standaa - Remember Monica over 8 years
    Thanks a lot for the input I have been tearing my hairs over this. Well, here we are now, four years later. The bug still isn't fixed in Android 6.0 & Nexus 5.
  • prom85
    prom85 over 8 years
    can I somehow retrieve the MediaStore.EXTRA_OUTPUT in onActivityResult or do I really have to remember it myself? I would have to persistantly save it so that my app even remembers it if it gets destroyed while a photo is taken...
  • Renaud Favier
    Renaud Favier about 8 years
    @Stanislasdrg I think I have the same problem right now, did you add anything to the list ? I have too a nexus 5 android 6.0 and the code here doen't seem to do it, even if I force to true the hasImageCaptureBug();
  • Renaud Favier
    Renaud Favier about 8 years
    @Stanislasdrg to be specific the uri gave me by this fix is content://media/external/images/media/40192 but I found the pic in /pictures/145613944835.jpg
  • Standaa - Remember Monica
    Standaa - Remember Monica about 8 years
    @RenaudFavier Did you properly add the nexus 5 in the list before ? Proper name is " devices.add("google/hammerhead/hammerhead") "
  • Renaud Favier
    Renaud Favier about 8 years
    @Stanislasdrg yes but I still got the problem, I finaly decided to use CommonsWare library, for now it works way better
  • Igor Janković
    Igor Janković almost 8 years
    It doesn't work on samsung galaxy note 3 with lolipop: ava.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.getScheme()' on a null object reference
  • truthadjustr
    truthadjustr about 4 years
    The above incomplete snippet does not merit the simplicity that it claimed. Surely, there are hidden complexities for the unfamiliar traveller. Such as scanning for presence or absence of the file. Such as wither it is publicly available or private to the app. Such as the need for a provider or getting a thumbnail only size. These are information gaps for the unwary traveller needs to be aware of.
  • julo
    julo over 3 years
    This seems to be working for the set of devices I have been working with, which is good. One downside is that, if the user cancels the camera activity, then we will have inserted an empty item into the media store that doesn't get cleaned up. So if you do a media store query, you'll get back a uri that will throw a file not found exception when you try to read its contents. I am currently working around that by making sure my media store query filters for size > 0.