Home > Android, Development, Java, Samsung Galaxy Tab > Null Intent passed back On Samsung Galaxy Tab…

Null Intent passed back On Samsung Galaxy Tab…

March 30, 2011

So, I’ve had a rough few days, and by rough, I’m talking about the equivilant of a goats knee kinda rough. And if you haven’t guessed it yet, it has to do with the Samsung Galaxy Tab(the 7 inch guy), and null intents when using the camera from inside your app.

Let me set the scene, first, and tell you my story.  I was über excited to recieve my very own “work purchased” Samsung galaxy tablet yesterday, to be used for, among other things, developing some Android applications. I couldn’t contain my excitement, and this was way more fun than filling in my timesheets, so, I cracked the sucker open and began developing a little something that basically made use of the camera functionality. So I initially set off to create a test Activity which had a Gallery control, and a button to activate the camera. All I wanted to do was create a sample app, which would populate the gallery with the photos that I’d take. However, I couldn’t help but be reminded of how similar grappling with the camera functionality on the Samsung Galaxy tab is issuing a command to my ridgeback dog Bruno. Neither of them does what you expect it to! Instead, you’re just left surprised while it does its own crazy thing!  :)

So in my example, I thought I’d try something simple. Just start the camera activity with the method call to startActivityForResult, and in the method onActivityResult, when the camera returns, I expected to be able to get both the thumbnail and the actual image in the intent’s data. Alas, this is not the case. It appears that if you populate the EXTRA_OUTPUT extra with a URI of where to store the new photo, then the camera will store the photo there for you. Now in my experience, this is true, however, with a little annoying caveat. Not only will the camera store it in the URI location that you supplied, but it also stores it in the default location of its own!

I had another problem. I wanted not only the thumbnail, but also the actual image. For the purpose of this, I didn’t really need the actual image, but more the location of where I could go and get it. Now when using the method I just previously spoke about, by providing the EXTRA_OUTPUT extra on the request intent, the camera on return to the onActivityResult, ends up passing back a NULL intent, which used to have the thumbnail image in the extra key called “data“. Now you have diddley squat!

Yet another problem I faced was that, the URI I was passing in the request intent, to tell the camera where to save this image, always ended up being NULL when the onActivityResult method was called. So now, not only do I have to somehow persist the URI that I had already passed into the request intent (to prevent it from becoming null), I now also didn’t have a thumbnail to speak of – which I used to get from the “data” extra in the return intent.

Plan B…..

It seemed that from all my reading, the best way was to create a temp file, that always had a deterministic name and location, create the file, then create a URI from that, and pass that as part of the request intent, and you could reconstruct the URI when the onActivityResult was called. Thats great, but where’s my thumbnail? Still not there? <sarcasm>Nice!</sarcasm>

Plan C….

So this is what I eventually had to resort to, in order to get both the thumbnail, AND actual image location in order to populate my gallery control with the thumbnails, and retain the actual image location so that I could display it when the user tapped on the thumbnail, and thereby also avoiding any duplicate images being stored :

Call the Camera activity as per normal Don’t do anything fancy, just create the intent with the ACTION_IMAGE_CAPTURE constant. That way, you don’t create duplicate images all over the place.

On the return call of onActivityResult, you need to do a managedQuery on the MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI and MediaStore.Images.Media.EXTERNAL_CONTENT_URI locations, and selecting the last image that was captured. This way, you are able to obtain a handle to both the thumbnail, and the image without having to deal with all the weirdness of before. Once you have these, then you’re good to go! I’ve posted some sample code below to show you what I’m talking about.


private static final int CAMERA_IMAGE_CAPTURE = 0;

public void btnTakePhoto_onClick(View view){
 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 startActivityForResult(intent, CAMERA_IMAGE_CAPTURE);
 }

&nbsp;

@Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 super.onActivityResult(requestCode, resultCode, data);
 if(requestCode==CAMERA_IMAGE_CAPTURE && resultCode==Activity.RESULT_OK){

// Describe the columns you'd like to have returned. Selecting from the Thumbnails location gives you both the Thumbnail Image ID, as well as the original image ID
String[] projection = {
 MediaStore.Images.Thumbnails._ID,  // The columns we want
 MediaStore.Images.Thumbnails.IMAGE_ID,
 MediaStore.Images.Thumbnails.KIND,
 MediaStore.Images.Thumbnails.DATA};
 String selection = MediaStore.Images.Thumbnails.KIND + "="  + // Select only mini's
 MediaStore.Images.Thumbnails.MINI_KIND;

 String sort = MediaStore.Images.Thumbnails._ID + " DESC";

//At the moment, this is a bit of a hack, as I'm returning ALL images, and just taking the latest one. There is a better way to narrow this down I think with a WHERE clause which is currently the selection variable
Cursor myCursor = this.managedQuery(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, projection, selection, null, sort);

long imageId = 0l;
long thumbnailImageId = 0l;
String thumbnailPath = "";

try{
 myCursor.moveToFirst();
imageId = myCursor.getLong(myCursor.getColumnIndexOrThrow(MediaStore.Images.Thumbnails.IMAGE_ID));
thumbnailImageId = myCursor.getLong(myCursor.getColumnIndexOrThrow(MediaStore.Images.Thumbnails._ID));
thumbnailPath = myCursor.getString(myCursor.getColumnIndexOrThrow(MediaStore.Images.Thumbnails.DATA));
}
finally{myCursor.close();}

 //Create new Cursor to obtain the file Path for the large image

 String[] largeFileProjection = {
 MediaStore.Images.ImageColumns._ID,
 MediaStore.Images.ImageColumns.DATA
 };

 String largeFileSort = MediaStore.Images.ImageColumns._ID + " DESC";
 myCursor = this.managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, largeFileProjection, null, null, largeFileSort);
String largeImagePath = "";

try{
 myCursor.moveToFirst();

//This will actually give yo uthe file path location of the image.
largeImagePath = myCursor.getString(myCursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATA));
}
finally{myCursor.close();}
 // These are the two URI's you'll be interested in. They give you a handle to the actual images
 Uri uriLargeImage = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, String.valueOf(imageId));
 Uri uriThumbnailImage = Uri.withAppendedPath(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, String.valueOf(thumbnailImageId));

// I've left out the remaining code, as all I do is assign the URI's to my own objects anyways...

}
}

I hope this helps someone else out there, cause this had me baffeled for a while. I don’t know what this just can’t be easier. Surely there’s no need to have to jump through hoops like this. Anyhoo, I’m working on getting the better managed query working, and I’ll just use this when I need it. It seems to work for the most part anyway.

About these ads
  1. Jaideep
    April 30, 2011 at 22:24 | #1

    This is just awesome! Great work.

    • May 3, 2011 at 09:17 | #2

      Thanks Jaideep – hope it helped.

  2. prakash
    May 13, 2011 at 09:46 | #3

    This is working fine for me the only problem is everytime i run his code i see album by name mnt which contains a broken image sdcard in my Galley. Do you know why does this happen

  3. Andrew
    June 3, 2011 at 17:26 | #4

    Than you SO much for your post.

    I just spent a full day fighting this stupid problem (Nexus S) and could’t figure out what was wrong.

    Your code compile and ran great first time.

    P.S. I notice a few warnings due to unused “largeImagePath” etc…. some code could be removed.

    There are a lot of people fighting with this it seems (check out StackOverflow), so I appreciate your post, you could put pointers to your post in those sites if you wanted some more traffic!

    Well done!

  4. August 11, 2011 at 22:36 | #5

    Nice post. Silly samsung.. you saved me a lot of time! Thank you

  5. Alexey
    October 5, 2011 at 10:31 | #6

    Thank you for great post. I have the same problem with Samsung galaxy phone.

  6. chetan
    December 6, 2011 at 12:48 | #7

    thanks alot to hint it.. :)
    i was thinkin my samsung tab is a smart phone.. :P

  7. pratik
    December 15, 2011 at 12:42 | #8

    I m facing the same problem Kevin,
    But somehow i feel that in some devices, the control is never returned to onActivityResult(), instead our activity starts over again from oncreate() after returning from camera….

    Well , wat i tried was, i passed
    MediaStore.EXTRA_OUTPUT, fileUri

    stored the image uri in a bundle,
    and retrieved this URi from the bundle in onActivityResult()

  8. December 28, 2011 at 17:51 | #9

    First of all, thank you for the solution, it is the only one I found.
    However it would be great if we could find a smoother solution.

  9. Long
    January 6, 2012 at 11:23 | #10

    Thank you very very much, Kevin. Worked like a charm.

  10. January 11, 2012 at 17:59 | #11

    I am trying this code out on a Nexus S running 2.3 and I face the following problems

    1) At the camera preview stage, I can’t find the pic in this media store. i.e \mnt\sdcard\DCIM\camera. I am not sure where this is stored.

    2) Since #1 can’t find an image, the latest image (says captured by a native camera) app gets deleted as that’s the latest

    3) After the latest file is deleted by our Java program, the Media Store’s indexing is not “refreshed”.. So if i re-use the camera intent again and If i query the Media Store again, the same file name (deleted in #2) shows up the next time too.. Despite the file being deleted from the file system.

    • January 11, 2012 at 18:03 | #12

      This code works fine on HTC Desire (and Desire S). Although in that case we could just use the simpler approach and directly read it from the input “Intent”

      http://stackoverflow.com/a/7130345/254823

      NexusS is such a pain to develop as I cant use EXTRA_OUTPUT as it creates a parallel copy of the image in the gallery (apart from the place EXTRA_OUTPUT defines).

  11. January 18, 2012 at 09:33 | #13
  12. Appdev
    March 15, 2012 at 18:25 | #14

    Thanks. It worked for samsung phone but It does not work for galaxy tab7510. Plc advise

  13. Frank
    March 29, 2012 at 12:19 | #15

    Thank You very much for Your solution, I have similar problem using Sony Ericsson Experia Active.

    It works, but I get the large Image in the thumbnail uri and vice versa !!??

    Any idea why?

    /Frank
    Sweden

  14. David Wu
    April 20, 2012 at 13:27 | #16

    good job, kevin. you save me a lot of time. it’s done on my Huawei U8860. thanks again. :)

  15. May 17, 2012 at 08:42 | #17

    Good Job, Kevin. You have solved a major issue. But, i have one issue, whenever i am capturing image from camera using intent, it is always returns the image in LANDSCAPE mode even though i capture the image in portrait mode. :):) This problem arises only in Samsung Galaxy. When i open the image in Gallery it is showing in PORTRAIT Mode. But if i open the image in DCIM folder it is showing in LANDSCAPE mode…. What might be the problem :(:( . How to set the orientation of the image that we are going to capture using intent ???

  16. September 11, 2012 at 13:06 | #18

    I ended up creating the image in a specific location following much of what Google lays out at http://developer.android.com/training/camera/photobasics.html. That is to say, create a file location that is passed into the image capture intent and only store it there. Use that same file location to render a scaled version on the fly for the thumbnail.

  1. March 20, 2012 at 13:07 | #1
  2. May 23, 2012 at 15:29 | #2
Comments are closed.
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: