1

I have complete working code to take a picture, crop it, send email with image attachment.

Here is the flow of the code: Take a picture > Find and select the picture we just took from Gallery > Crop it > Email attachment.

I would like to find out how I can skip the whole "Find and select the picture we just took from Gallery" part out and just go straight into cropping immediately after taking the picture.

Is there an Intent that can do what I am requesting?

I've tried this tutorial but it still makes the user go into gallery and look for the picture they just took http://www.londatiga.net/featured-articles/how-to-select-and-crop-image-on-android/

EDIT I should also mention that I have a custom view when I am taking pictures so Im not sure if I can use Intent MediaStore.ACTION_IMAGE_CAPTURE

Here is the code that executes the cropping AND also makes you choose the image you just took by looking in the gallery.

        Intent crop_pic = new Intent(Intent.ACTION_PICK, uriTarget); // used to be ACTION_GET_CONTENT /ACTION_PICK
           crop_pic.putExtra("crop", "true");                           // this enables crop feature                                                        
           //crop_pic.putExtra("aspectX", 200);                         // this defines the aspect ration
           //crop_pic.putExtra("aspectY", 150);                     
           //crop_pic.putExtra("outputX", 500);                         // this defines the output bitmap size
           //crop_pic.putExtra("outputY", 500);     
           //crop_pic.putExtra("scale", true);
  try {    crop_pic.putExtra("return-data", false);                     // true to return a Bitmap, false to directly save the cropped iamge                            
           crop_pic.putExtra(MediaStore.EXTRA_OUTPUT, uriTarget);       // save output image in uri     
           //crop_pic.setDataAndType(uriTarget, "image/*");             // this will open all images in the Galery
           startActivityForResult(crop_pic, EMAIL_PIC);
           overridePendingTransition(R.anim.fadein, R.anim.fadeout);    
         } 
  catch    (ActivityNotFoundException e) {
           toast.showToastDialog(getApplicationContext(), e.toString());    
         }   
  LOGCAT.DEBUG(TAG, "Cropping Picture");

  }

Is there an Intent im not aware of that can just start cropping if I supply it with a uri Target?

Here is the full code:

 public class DigitalSignature extends Activity implements SurfaceHolder.Callback{

            Camera    camera;
     SurfaceHolder    surfaceHolder;
           boolean    previewing = false;

               Uri    uriTarget;
          String[]    recipients = new String[]{"test@gmail.com", "",}; 
         final int    CROP_PIC = 1, EMAIL_PIC = 2, RETURN_ACTIVITY = 3, FINISH = 4, INDEX_GALLERY = 5;  

     final  String    TAG = "Digital Signature";

     ToastBuilder     toast = new ToastBuilder(this);
     ProjectDebug     LOGCAT = new ProjectDebug();  
 ProgressDialogManager    pDialog = new ProgressDialogManager();




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

                DigitalSignature.this.requestWindowFeature(Window.FEATURE_NO_TITLE);         
                DigitalSignature.this.setContentView(R.layout.digitalsignature);
                DigitalSignature.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);   

                DigitalSignature.this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);            
                DigitalSignature.this.getWindow().setFormat(PixelFormat.TRANSLUCENT); // Used to be TRANSPARENT



                SurfaceView     surfaceView = (SurfaceView)findViewById(R.id.camerapreview);
                                surfaceHolder = surfaceView.getHolder();
                                surfaceHolder.addCallback(DigitalSignature.this);
                                surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

                LayoutInflater  controlInflater = LayoutInflater.from(getApplicationContext());
                View            viewControl = controlInflater.inflate(R.layout.control, null);
                LayoutParams    layoutParamsControl = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);

                DigitalSignature.this.addContentView(viewControl, layoutParamsControl); // Applying Filters and Parameters


                TextView        alignsig = (TextView)findViewById(R.id.alignsig);
                                alignsig.setText("Please take picture of the signature for" + "\n" + "Job Number " + DIGIJOB);

                Button          buttonTakePicture = (Button)findViewById(R.id.takepicture);
                                buttonTakePicture.setOnClickListener(new Button.OnClickListener(){



        @Override
        public void onClick(View arg0) {    
            LOGCAT.DEBUG(TAG, "Taking Picture");


                    // Sets camera monochrome filter ( Black and Whiite )
                    Camera.Parameters parameters = camera.getParameters();        
                    parameters.setColorEffect(android.hardware.Camera.Parameters.EFFECT_MONO);
                    camera.setParameters(parameters);        
                    camera.startPreview(); 


                    // Initiate Camera Focus
                    camera.autoFocus(mAutoFocusCallback);

                    // Creates Directory to save image in gallery
                    uriTarget = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, new ContentValues());   

                    //Notify the MediaScanner that a new file has been added and should be indexed so it shows up in the MediaStore.
                    Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                    intent.setData(uriTarget);
                    sendBroadcast(intent);

                    }
             });
        }






Camera.AutoFocusCallback mAutoFocusCallback = new Camera.AutoFocusCallback() {

    @Override
    public void onAutoFocus(boolean success, Camera camera) {

        camera.takePicture(myShutterCallback, myPictureCallback_RAW, myPictureCallback_JPG);
    }
    };


ShutterCallback myShutterCallback = new ShutterCallback(){

    @Override
    public void onShutter() {

    }};


PictureCallback myPictureCallback_RAW = new PictureCallback(){

    @Override
    public void onPictureTaken(byte[] arg0, Camera arg1) {

    }};


PictureCallback myPictureCallback_JPG = new PictureCallback(){

    @Override
    public void onPictureTaken(byte[] arg0, Camera arg1) {
        //bmp = BitmapFactory.decodeByteArray(arg0, 0, arg0.length);    

        cropPic();

   OutputStream imageFileOS;
        try {   
                imageFileOS = getContentResolver().openOutputStream(uriTarget); 
                imageFileOS.write(arg0);
                imageFileOS.flush();
                imageFileOS.close();

                toast.showToastDialog(getApplicationContext(), "Image saved: " + uriTarget.toString());                             
            } 


      catch     (IOException e) {
                toast.showToastDialog(getApplicationContext(), e.toString());   
            }       

            // camera.startPreview(); // No need to start preview, we are saving
            }};                       // picture and no longer initiating camera






   protected void onActivityResult(int requestCode, int resultCode, Intent data) {

     switch     (requestCode) {

        case      CROP_PIC:
                    cropPic();
                    LOGCAT.DEBUG(TAG, "cropPic Case");      
       break;


        case      EMAIL_PIC:
                    emailPic();
                    LOGCAT.DEBUG(TAG, "emailPic Case");         
        break;                                                                  


        case      RETURN_ACTIVITY:  
                    returnActivity();
                    LOGCAT.DEBUG(TAG, "returnActivity Case");           
        break; 
            }       
    }



  private void cropPic() {  


    Intent crop_pic = new Intent(Intent.ACTION_PICK, uriTarget); // used to be ACTION_GET_CONTENT /ACTION_PICK
           crop_pic.putExtra("crop", "true");                           // this enables crop feature                                                        
           //crop_pic.putExtra("aspectX", 200);                         // this defines the aspect ration
           //crop_pic.putExtra("aspectY", 150);                     
           //crop_pic.putExtra("outputX", 500);                         // this defines the output bitmap size
           //crop_pic.putExtra("outputY", 500);     
           //crop_pic.putExtra("scale", true);
  try {    crop_pic.putExtra("return-data", false);                     // true to return a Bitmap, false to directly save the cropped iamge                            
           crop_pic.putExtra(MediaStore.EXTRA_OUTPUT, uriTarget);       // save output image in uri     
           //crop_pic.setDataAndType(uriTarget, "image/*");             // this will open all images in the Galery
           startActivityForResult(crop_pic, EMAIL_PIC);
           overridePendingTransition(R.anim.fadein, R.anim.fadeout);    
         } 
  catch    (ActivityNotFoundException e) {
           toast.showToastDialog(getApplicationContext(), e.toString());    
         }   
  LOGCAT.DEBUG(TAG, "Cropping Picture");

  }



  private void emailPic(){

     // new email_pic().execute();     

        GMailSender m = new GMailSender("username@gmail.com", "testtest");

        String[] toArr = { "sendingto@gmail.com" };
        m.setTo(toArr);
        m.setFrom("sendingto@gmail.com");
        m.setSubject("This is an email sent using my Mail JavaMail wrapper from an Android device.");
        m.setBody("Email body.");

        try { 
          m.addAttachment(getRealPathFromURI(uriTarget)); 

          if(m.send()) { 
            Toast.makeText(DigitalSignature.this, "Email was sent successfully.", Toast.LENGTH_LONG).show(); 
          } else { 
            Toast.makeText(DigitalSignature.this, "Email was not sent.", Toast.LENGTH_LONG).show(); 
          } 

        } catch(Exception e) {
          //Toast.makeText(MailApp.this, "There was a problem sending the email.", Toast.LENGTH_LONG).show(); 
          Log.e("MailApp", "Could not send email", e); 
        } 


      // OLD METHOD OF SENDING EMAIL
    /*  Intent email_pic = new Intent(Intent.ACTION_SEND);      // Commence choose photo action         
        email_pic.putExtra(Intent.EXTRA_EMAIL, recipients);     // Inserts Digital Signature address automatically in recipient
        email_pic.putExtra(Intent.EXTRA_SUBJECT, DIGIJOB);      // Inserts Track Number automatically in subject
        email_pic.putExtra(Intent.EXTRA_STREAM, uriTarget);     // Attaches image to Gmail
        email_pic.setType("image/jpeg");                        // Converts image to jpeg
        startActivityForResult(email_pic, RETURN_ACTIVITY); 
        overridePendingTransition(R.anim.fadein, R.anim.fadeout);   */
        JOB = "job";                                            // Reset job variable from track no (1736) to "job" so                                                                          
        LOGCAT.DEBUG(TAG, "Emailing Picture");                      // when you go back into Jobs Activity, it can parse data.

      returnActivity();

  }






  private void returnActivity() {
      Intent email_pic = new Intent(DigitalSignature.this, AgentPortalActivity.class);
      startActivityForResult(email_pic, FINISH);
      overridePendingTransition(R.anim.fadein, R.anim.fadeout);      
      LOGCAT.DEBUG(TAG, "Returning Activity");
  }





  private String getRealPathFromURI(Uri uriTarget) {

                String[] projection = { MediaStore.Images.Media.DATA };
                @SuppressWarnings("deprecation")
                Cursor cursor = managedQuery(uriTarget, projection, null, null, null);
                int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                                    cursor.moveToFirst();     

            LOGCAT.DEBUG("getRealPathFromURI", cursor.getString(column_index));
            return cursor.getString(column_index);

}



@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {


    if    (previewing){
              camera.stopPreview();
              previewing = false;
          }

    if    (camera != null){

    try { camera.setPreviewDisplay(surfaceHolder);
          camera.startPreview();
          previewing = true;
          } 

    catch (IOException e) {
            toast.showToastDialog(getApplicationContext(), e.toString());   
          }
      }
  }




@Override
public void surfaceCreated(SurfaceHolder holder) {

    camera = Camera.open(); 
    //camera = Camera.open(0);  

    }




@Override
public void surfaceDestroyed(SurfaceHolder holder) {

    camera.stopPreview();
    camera.release();
    camera = null;
    previewing = false;
}
 }
John Nguyen
  • 547
  • 1
  • 11
  • 25
  • You have `myPictureCallback.onPictureTaken()` which can process the Jpeg stream in **byte[] arg0**. There is no `MediaStore.ACTION_IMAGE_CAPTURE` intent as far as I understand, therefore the solution by [@whizzle](http://stackoverflow.com/users/1687169/whizzle) is not applicable. – Alex Cohn Mar 04 '14 at 16:11
  • Thank you for your valuable feedback Alex! Do you have any opinions on how I should Handle this? – John Nguyen Mar 04 '14 at 16:56
  • You can use [LLJTran](http://mediachest.sourceforge.net/mediautil/) to crop your Jpeg `byte[]` efficiently and losslessly (but with some limitations). – Alex Cohn Mar 04 '14 at 17:29

2 Answers2

1

You just need to use the MediaStore.ACTION_IMAGE_CAPTURE intent action. That will launch the camera. And in your onActivityResult you can get a the image that was just taken. Now you can crop it and send it and whatever else you'd like. Below is an example.

Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File pics = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File cameraPhotoFile = new File(pics, System.currentTimeMillis()+".jpg");
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cameraPhotoFile));
startActivityForResult(cameraIntent, REQUEST_CODE_CAMERA);


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode != Activity.RESULT_OK) return;
    if (requestCode == REQUEST_CODE_CAMERA) {
        MediaScannerConnection.scanFile(this,
                new String[]{cameraPhotoFile.getAbsolutePath()}, null, null);

// do something with the image located at cameraPhotoFile

I've included the MediaScannerConnection because the image will not show up in the gallery until it gets scanned. You can remove that if you want, but I like images that were taken to show up. The bitmap is now located in the "cameraPhotoFile." Use that file however you like.

Also, here are the docs from Google about this called Taking Photos Simply. It's pretty straight forward

whizzle
  • 2,053
  • 17
  • 21
  • That is a great suggestion. However, I have a custom view when I call for camera in my code. If I call MediaStore.ACTION_IMAGE_CAPTURE, will I still be able to use my custom view? – John Nguyen Mar 03 '14 at 20:25
  • of course you can :) but it actually doesn't sound like you have a custom "view". It sounds like you might have built a custom camera. In that case, why are you using the gallery in the first place? You know what the image is getting stored. Just use BitmapFactory to decode the image and bypass the gallery. However, I highly recommend you don't build a custom camera unless you're working on a camera app. – whizzle Mar 04 '14 at 00:15
  • Thanks for your reply whizzle, you are right, I do have a custom camera, not a custom view. My apologies, I didnt know there is a difference. So, what you are suggesting is, in public void onPictureTaken(byte[] arg0, Camera arg1), I should call Bitmap bmp = BitmapFactory.decodeByteArray(arg0, 0, arg0.length); and do initiate the cropping right there? How would I call the Intent to crop and direct it to Bitmap bmp? – John Nguyen Mar 04 '14 at 00:33
  • I would like to have a custom camera, because it contains some information for the end user whereas if i use the predefined camera, I cannot include data i want my users to see. – John Nguyen Mar 04 '14 at 00:35
  • Yes, decodeByteArray but it looks like you were already doing that. Did it not work? – whizzle Mar 04 '14 at 00:39
  • I didnt know where to go from there. lol sorry, I'm still a novice. Ill read up on the link you provided me shortly. So, my question now is how would I call the crop intent and point it to bitmap bmp? Thank you for all your help and sorry if I am frustrating you with questions only a beginner would ask. :) – John Nguyen Mar 04 '14 at 00:44
  • There is no "official" crop intent. You can either find out if the AOSP camera can handle it by checking if the packageManager can handle the intent such as if (cropIntent.resolveActivity(getPackageManager()) != null) or you need to build that yourself. Check out this here: http://commonsware.com/blog/2013/01/23/no-android-does-not-have-crop-intent.html – whizzle Mar 04 '14 at 00:48
  • I've read that awesome article before. Before reading that article, i also used to initiate crop by doing "com.android.camera.action.CROP" since then, I now do it by including crop_pic.putExtra("crop", "true") in my intent. I just downloaded a sample project from the link you gave me. – John Nguyen Mar 04 '14 at 00:58
  • That sample project is too sophisticated and elegant for me. I guess what I am asking for cannot be done unless I do some major code revision. I thought it would be something simple like a 1 liner lol. Thanks for your help whizzle, I'll wait around and see if anyone has anything to say, if not, I'll accept your answer as correct when the bounty is almost over. Also, let me know if you have any other information you can enlighten me with! THanks! – John Nguyen Mar 04 '14 at 01:39
  • Check out this information here for a 1 liner crop intent: http://stackoverflow.com/questions/18463102/crop-an-image-by-passing-the-image-file-path-in-android Again, I suggest you check the packagemanger to see if the system can handle that intent. – whizzle Mar 04 '14 at 01:44
  • THank you for that link whizzle, I got it automatically do crop by just calling crop_pic = new Intent(); and crop_pic.putExtra("crop", "true"); By doing so, When I call crop now, It asks me to "Choose action" and crop from gallery is an option, I just set that as default and I include the uri target in the intent so It doesnt ask me again and goes directly to cropping without the need to select the image from gallery. thanks again for your help! – John Nguyen Mar 04 '14 at 18:23
  • i also included a try catch to see if the users device can support cropping like you suggested. – John Nguyen Mar 04 '14 at 21:05
  • Sorry, I thought I had already awarded you the bounty. Anyways, thanks again! – John Nguyen Mar 07 '14 at 19:34
0

The easiest solution i can think of is:

  1. Call the ACTION_IMAGE_CAPTURE Intent to take a picture with the Stock Camera
  2. onActivityResult you will get the content uri of the new picture over data.getData()
  3. Do it like vogella here http://blog.vogella.com/2011/09/13/android-how-to-get-an-image-via-an-intent/ and directly open an inputstream to the new picture

no need for the pick intent.

Rene M.
  • 2,660
  • 15
  • 24
  • i have one question with this solution, I have a custom view setup when I am in the camera activity. Will I be able to use my custom view still if I call ACTION_IMAGE_CAPTURE intent? – John Nguyen Mar 03 '14 at 20:26
  • If you have a custom view you have also the binary data of the picture. then i don't understand your question – Rene M. Mar 03 '14 at 23:02
  • I do have the data of the picture, but when I request cropPic() function, I have to select the picture I just took from the Gallery. But, what I am also saying is, If I were to attempt your suggestion, I think I would lost the ability to customize my camera layout. – John Nguyen Mar 03 '14 at 23:30