Friday 14 December 2012

Photo capture Intent causes NullPointerException on Samsung phones only

Hello Dorid Guys,

Yesterday, I came across  a weird situation . In my application ,I am capturing an image from camera and displaying it inside an imageview. I tested its functionality on many of android device and its working 
fine except "Samsung Galaxy Tab".
In Samsung device the capture intent will be "Null" and I am getting null pointer exception while capturing image from camera in android Samsung S2.

Following code I am using which running fine in all devices except Samsung :

For capturing Image from camera:

Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(captureIntent, CAMERA_CAPTURE);

For choosing an image from gallery:

Intent gallaryIntent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(gallaryIntent, RESULT_LOAD_IMAGE);


And finally the onActivityResult(int requestCode, int resultCode, Intent data) :

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
       
       if (requestCode == RESULT_LOAD_IMAGE) {
         picUri = data.getData();
         performCrop();
        }

       if (requestCode == CAMERA_CAPTURE) {
  // get the Uri for the captured image
  picUri = data.getData();
  if (picUri != null) {
   performCrop();
  }
 }
 // user is returning from cropping the image
  if (requestCode == PIC_CROP) {
  // get the returned data
  Bundle extras = data.getExtras();
  // get the cropped bitmap
  rectangleBitmap = extras.getParcelable("data");
  // retrieve a reference to the ImageView
  GraphicsUtil graphicsUtil = new GraphicsUtil();
  rectangleBitmap = graphicsUtil.getRoundedCornerBitmap(
  rectangleBitmap, 16);
  ImageView picView = (ImageView) findViewById(R.id.imgView);
  // display the returned cropped image
  picView.setImageBitmap(rectangleBitmap);
  btnMakeMotekon.setBackgroundDrawable(getResources()
                .getDrawable(R.drawable.make));
  btnMakeMotekon.setEnabled(true);
  btnNext.setEnabled(true);
 }
}
}

Now the crop image method, picking the image from gallery or camera and crop it.


private void performCrop() {
 // take care of exceptions
 try {
 // call the standard crop action intent (the user device may not support it)
 Intent cropIntent = new Intent("com.android.camera.action.CROP");
 // indicate image type and Uri
 cropIntent.setDataAndType(picUri, "image/*");
 // set crop properties
 cropIntent.putExtra("crop", "true");
 // indicate aspect of desired crop
 cropIntent.putExtra("aspectX", 1);
 cropIntent.putExtra("aspectY", 1);
 // indicate output X and Y
 cropIntent.putExtra("outputX", 256);
 cropIntent.putExtra("outputY", 256);
 // retrieve data on return
 cropIntent.putExtra("return-data", true);
 // start the activity - we handle returning in onActivityResult
 startActivityForResult(cropIntent, PIC_CROP);
 }catch (ActivityNotFoundException anfe) {
  // display an error message
  String errorMessage = "Whoops - your device doesn't support the 
                   crop action!";
  Toast toast = Toast.makeText(this, errorMessage,
                    Toast.LENGTH_SHORT);
  toast.show();
 }
}


The above code is working fine in most of the devices but when test it on Samsung S2 . I am getting the NullPointerException.
This is very tough time for me because my client wants to release the Application ASAP. And because of this issue he is delaying its time. 

After spending a full day and a night a found the fix for this problem, I added some extra code inside the
onActivityResult method.

The trick is:

if(picUri != null) {
    // do it the normal way
else {
    // do it the "Samsung" way
}



And it works :)
Here is my source code:


 protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
   super.onActivityResult(requestCode, resultCode, data);  
  if (resultCode == RESULT_OK) {  
  if (requestCode == RESULT_LOAD_IMAGE) {  
    picUri = data.getData();  
    performCrop();  
  }  
  if (requestCode == CAMERA_CAPTURE) {  
  // get the Uri for the captured image  
  picUri = data.getData();  
  /*  
  * In samsung , the picUri will be null and application will  
  * give runtime error In else part we are doing the code for  
  * samsung device  
  */  
  if (picUri != null) {  
    // do it the normal way  
    performCrop();  
  } else {  
   // 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));  
  picUri = uriLargeImage;  
  performCrop();  
  }  
  }  
 }  

Hope this will helps Some one.
Enjoy Coding :)

Mukesh Kumar

Hi Guys I am from Delhi working as Web/Mobile Application Developer(Android Developer), also have knowledge of Roboelctric and Mockito ,android test driven development... Blogging has been my passion and I think blogging is one of the powerful medium to share knowledge and ideas....

22 comments:

  1. Nice! hope it will work fine on my client device..

    ReplyDelete
  2. Thanks Manish,
    I believe, its worked fine on your client device.

    ReplyDelete
  3. first, thank you, that you published your solution :D

    But the constant RESULT_LOAD_IMAGE CAMERA_CAPTURE PIC_CROP doesn't exist.

    ReplyDelete
  4. Pleas tell me what i have to import to get this constants

    ReplyDelete
  5. Hello ,
    This is nothing only the request code, which I assigned as constant inside my class. like this...

    public class ImageCapture extends Activity {
    final int CAMERA_CAPTURE = 1;
    final int PIC_CROP = 2;
    private static int RESULT_LOAD_IMAGE =3;

    @Override
    public void onCreate(Bundle
    savedInstanceState) {
    //you camera and gallery code
    }

    //Here put the above onActivityResult code
    }

    Hope, Now you got it...

    ReplyDelete
  6. Ok thank you.

    One more Question: where is the best place to put the Cod (to view the image in an Image View).

    ImageView image = (ImageView) findViewById(R.id.image);
    Bitmap myBitmap = BitmapFactory.decodeFile(path);
    Bitmap mySmalBitmap = Bitmap.createScaledBitmap(myBitmap, 250, 325,
    false);
    image.setImageBitmap(mySmalBitmap);
    image.setMaxHeight(88);
    image.setMaxWidth(50);

    (path must be replaced through the real path :D)

    ReplyDelete
  7. First of all let me know if you are using the cropping in your code , after picking the image from the gallery or camera ??

    The same way as I show in above code?

    ReplyDelete
  8. sorry for the stupid question, but i don't know the function of "crop"

    ReplyDelete
  9. Okay, Its mean you ate not cropping the pictuer.
    Then, in this case best way is to place your code inside onActivityResult().Also add null check code for image path and bitmap.

    ReplyDelete
  10. so i call my method when you call performCrop();

    ReplyDelete
  11. Right, and also remember to pass the parameter "path" in that function.

    ReplyDelete
  12. Ok Thank you again.

    ReplyDelete
  13. hi thanks for your coding....But in my samsung galaxy y mobile the core option not working..i.e cropping view and save option every thing working but after crop the gallery image not cropped ...it's showing the as usel image

    ReplyDelete
  14. Hello kumaravel,

    I have tested the above code samsung S2,s3, galaxy duaos and htc and It works fine for me on all this devices. May be something is going wrong at your end...if you provide me your cropping code then I will help you.

    ReplyDelete
  15. Hello.. I wanna ask you..

    Where is the best way to put the code to view the image in ImageView? after doing the cropping thing.

    Thanks before, your code really work (even for samsung)

    ReplyDelete
  16. If you are doing the cropping then I think its good to place the code after the cropping things....as I did above because the cropping will work on both case either taking the picture from camera or by gallery.So making this a separate method and called startActivityForResult()...

    Hope you understand it.

    ReplyDelete
  17. Thanks for this solution... I wonder that no one has provided the solution.. And whats the exact problem with Samsung? why it gives null??

    I will try your solution and will get back to you if there's any problem. Thanks again :)

    ReplyDelete
  18. Hey mukesh, Nokia X (Android)having same problem..is there any alternative for that.

    ReplyDelete
  19. Hello Deependra.
    check this link for alternative solution:
    http://www.androiddevelopersolution.com/2014/02/android-capture-image-from-camera-and.html

    ReplyDelete
  20. Hi mukesh I am not getting Uri via picUri = data.getData();
    Any solution of that?

    ReplyDelete
  21. Hi mukesh i am not getting Uri in picUri = data.getData();
    Any solution for that?
    Thanks

    ReplyDelete

 

Copyright @ 2013 Android Developers Blog.