3

I have my code below that on click opens the camera, takes a photo, gets the photo from the camera, then puts in into a imageview. However i want to take the image and apply Text (Some sort of time stamp, either the time stamp from the image preferably, or just the system datetime) on the image and save as a jpeg.

If anyone can help me out that would be awesome.

public class PhotoIntentActivity extends Activity {

private static final int ACTION_TAKE_PHOTO_B = 1;

private static final String BITMAP_STORAGE_KEY = "viewbitmap";
private static final String IMAGEVIEW_VISIBILITY_STORAGE_KEY =  "imageviewvisibility";
private ImageView mImageView;
private Bitmap mImageBitmap;

private String mCurrentPhotoPath;

private static final String JPEG_FILE_PREFIX = "IMG_";
private static final String JPEG_FILE_SUFFIX = ".jpg";

private AlbumStorageDirFactory mAlbumStorageDirFactory = null;


/* Photo album for this application */
private String getAlbumName() {
    return getString(R.string.album_name);
}


private File getAlbumDir() {
    File storageDir = null;

    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {

        storageDir = mAlbumStorageDirFactory.getAlbumStorageDir(getAlbumName());

        if (storageDir != null) {
            if (! storageDir.mkdirs()) {
                if (! storageDir.exists()){
                    Log.d("CameraSample", "failed to create directory");
                    return null;
                }
            }
        }

    } else {
        Log.v(getString(R.string.app_name), "External storage is not mounted READ/WRITE.");
    }

    return storageDir;
}

private File createImageFile() throws IOException {
    // Create an image file name

    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
    File albumF = getAlbumDir();
    File imageF = File.createTempFile(imageFileName, JPEG_FILE_SUFFIX, albumF);

    return imageF;
}

private File setUpPhotoFile() throws IOException {

    File f = createImageFile();
    mCurrentPhotoPath = f.getAbsolutePath();

    return f;
}

private void setPic() {

    /* There isn't enough memory to open up more than a couple camera photos */
    /* So pre-scale the target bitmap into which the file is decoded */

    /* Get the size of the ImageView */
    int targetW = mImageView.getWidth();
    int targetH = mImageView.getHeight();

    /* Get the size of the image */
    BitmapFactory.Options bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
    int photoW = bmOptions.outWidth;
    int photoH = bmOptions.outHeight;

    /* Figure out which way needs to be reduced less */
    int scaleFactor = 1;
    if ((targetW > 0) || (targetH > 0)) {
        scaleFactor = Math.min(photoW/targetW, photoH/targetH); 
    }

    /* Set bitmap options to scale the image decode target */
    bmOptions.inJustDecodeBounds = false;
    bmOptions.inSampleSize = scaleFactor;
    bmOptions.inPurgeable = true;

 /* NEWELY ADDED CODE */
    /* Decode the JPEG file into a Bitmap */
    Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); 
    Bitmap replacedBitmap = timestampItAndSave(bitmap); 

    /* Associate the Bitmap to the ImageView */
    mImageView.setImageBitmap(replacedBitmap); 
    mImageView.setVisibility(View.VISIBLE);
 /* NEWELY ADDED CODE */
}

private void galleryAddPic() {
        Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
        File f = new File(mCurrentPhotoPath);
        Uri contentUri = Uri.fromFile(f);
        mediaScanIntent.setData(contentUri);
        this.sendBroadcast(mediaScanIntent);
}

private void dispatchTakePictureIntent(int actionCode) {

    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

    switch(actionCode) {
    case ACTION_TAKE_PHOTO_B:
        File f = null;

        try {
            f = setUpPhotoFile();
            mCurrentPhotoPath = f.getAbsolutePath();
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
        } catch (IOException e) {
            e.printStackTrace();
            f = null;
            mCurrentPhotoPath = null;
        }
        break;

    default:
        break;          
    } // switch

    startActivityForResult(takePictureIntent, actionCode);
}

private void handleBigCameraPhoto() {

    if (mCurrentPhotoPath != null) {
        setPic();
        galleryAddPic();
        mCurrentPhotoPath = null;
    }

}


Button.OnClickListener mTakePicOnClickListener = 
    new Button.OnClickListener() {
    public void onClick(View v) {
        dispatchTakePictureIntent(ACTION_TAKE_PHOTO_B);
    }
};


/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    mImageView = (ImageView) findViewById(R.id.imageView1);
    mImageBitmap = null;

    Button picBtn = (Button) findViewById(R.id.btnIntend);
    setBtnListenerOrDisable( 
            picBtn, 
            mTakePicOnClickListener,
            MediaStore.ACTION_IMAGE_CAPTURE
    );

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
        mAlbumStorageDirFactory = new FroyoAlbumDirFactory();
    } else {
        mAlbumStorageDirFactory = new BaseAlbumDirFactory();
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
    case ACTION_TAKE_PHOTO_B: {
        if (resultCode == RESULT_OK) {
            handleBigCameraPhoto();
        }
        break;
    } // ACTION_TAKE_PHOTO_B
  }
}

// Some lifecycle callbacks so that the image can survive orientation change
@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putParcelable(BITMAP_STORAGE_KEY, mImageBitmap);
    outState.putBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY, (mImageBitmap != null) );
    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    mImageBitmap = savedInstanceState.getParcelable(BITMAP_STORAGE_KEY);
    mImageView.setImageBitmap(mImageBitmap);
    mImageView.setVisibility(
            savedInstanceState.getBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY) ? 
                    ImageView.VISIBLE : ImageView.INVISIBLE
    );

}

/**
 * Indicates whether the specified action can be used as an intent. This
 * method queries the package manager for installed packages that can
 * respond to an intent with the specified action. If no suitable package is
 * found, this method returns false.
 * http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
 *
 * @param context The application's environment.
 * @param action The Intent action to check for availability.
 *
 * @return True if an Intent with the specified action can be sent and
 *         responded to, false otherwise.
 */
public static boolean isIntentAvailable(Context context, String action) {
    final PackageManager packageManager = context.getPackageManager();
    final Intent intent = new Intent(action);
    List<ResolveInfo> list =
        packageManager.queryIntentActivities(intent,
                PackageManager.MATCH_DEFAULT_ONLY);
    return list.size() > 0;
}

private void setBtnListenerOrDisable( 
        Button btn, 
        Button.OnClickListener onClickListener,
        String intentName
) {
    if (isIntentAvailable(this, intentName)) {
        btn.setOnClickListener(onClickListener);            
    } else {
        btn.setText( 
            getText(R.string.cannot).toString() + " " + btn.getText());
        btn.setClickable(false);
    }
}

  }

Getting error on 4.0 emulators. Log Cat.

 07-12 20:53:50.510: D/gralloc_goldfish(545): Emulator without GPU emulation detected.
 07-12 20:53:54.861: W/IInputConnectionWrapper(545): showStatusIcon on inactive      InputConnection
 07-12 20:54:00.700: D/dalvikvm(545): GC_FOR_ALLOC freed 114K, 3% free 10052K/10311K,      paused 217ms
 07-12 20:54:00.710: I/dalvikvm-heap(545): Grow heap (frag case) to 11.072MB for           1228816-byte allocation
 07-12 20:54:00.860: D/dalvikvm(545): GC_CONCURRENT freed 3K, 3% free 11249K/11527K,      paused 4ms+3ms
 07-12 20:54:00.960: D/AndroidRuntime(545): Shutting down VM
 07-12 20:54:00.960: W/dalvikvm(545): threadid=1: thread exiting with uncaught      exception (group=0x409961f8)
 07-12 20:54:01.000: E/AndroidRuntime(545): FATAL EXCEPTION: main
 07-12 20:54:01.000: E/AndroidRuntime(545): java.lang.RuntimeException: Failure      delivering result ResultInfo{who=null, request=1, result=-1, data=null} to activity      {com.example.android.photobyintent/com.example.android.photobyintent.PhotoIntentActivity}:      java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor
 07-12 20:54:01.000: E/AndroidRuntime(545):     at      android.app.ActivityThread.deliverResults(ActivityThread.java:2976)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at      android.app.ActivityThread.handleSendResult(ActivityThread.java:3019)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at      android.app.ActivityThread.access$1100(ActivityThread.java:122)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at      android.app.ActivityThread$H.handleMessage(ActivityThread.java:1176)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at android.os.Handler.dispatchMessage(Handler.java:99)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at android.os.Looper.loop(Looper.java:137)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at android.app.ActivityThread.main(ActivityThread.java:4340)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at java.lang.reflect.Method.invokeNative(Native Method)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at java.lang.reflect.Method.invoke(Method.java:511)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at dalvik.system.NativeStart.main(Native Method)
 07-12 20:54:01.000: E/AndroidRuntime(545): Caused by: java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor
 07-12 20:54:01.000: E/AndroidRuntime(545):     at android.graphics.Canvas.<init>  (Canvas.java:133)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at com.example.android.photobyintent.PhotoIntentActivity.timestampItAndSave(PhotoIntentActivity.java:103)
 07-12 20:54:01.000: E/AndroidRuntime(545):     at com.example.android.photobyinten

t.PhotoIntentActivity.setPic(PhotoIntentActivity.java:154) 07-12 20:54:01.000: E/AndroidRuntime(545): at com.example.android.photobyintent.PhotoIntentActivity.handleBigCameraPhoto(PhotoIntentActivity.java:199) 07-12 20:54:01.000: E/AndroidRuntime(545): at com.example.android.photobyintent.PhotoIntentActivity.onActivityResult(PhotoIntentActivity.java:244) 07-12 20:54:01.000: E/AndroidRuntime(545): at android.app.Activity.dispatchActivityResult(Activity.java:4649) 07-12 20:54:01.000: E/AndroidRuntime(545): at android.app.ActivityThread.deliverResults(ActivityThread.java:2972) 07-12 20:54:01.000: E/AndroidRuntime(545): ... 11 more

Updated code with draw bit map.

  private Bitmap timestampItAndSave(Bitmap toEdit){
    Bitmap dest = Bitmap.createBitmap(toEdit.getWidth(), toEdit.getHeight(), Bitmap.Config.ARGB_8888);

       SimpleDateFormat sdf = new     SimpleDateFormat("YYYY-MM-DD HH:MM:SS");
      String dateTime = sdf.format(Calendar.getInstance().getTime()); // reading local time in the system

    Canvas cs = new Canvas(dest);
    Paint tPaint = new Paint();
    tPaint.setTextSize(35);
    tPaint.setColor(Color.BLUE);
    tPaint.setStyle(Style.FILL);
    float height = tPaint.measureText("yY");
    cs.drawText(dateTime, 20f, height+15f, tPaint);
    cs.drawBitmap(dest,0 ,0,tPaint);
   try {
        dest.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/timestamped")));

    } catch (FileNotFoundException e) {
    e.printStackTrace();
    return null;
  }
    return dest;
}
Shailendr singh
  • 686
  • 6
  • 19
Jaison Brooks
  • 5,816
  • 7
  • 43
  • 79

1 Answers1

6

You don't want to put that in the onDraw method, as that's called whenever the View is invalidated (which you don't want).

Just create a method and put that code inside it. Call it after you've gotten the image, like so:

private Bitmap timestampItAndSave(Bitmap toEdit){
    Bitmap dest = Bitmap.createBitmap(toEdit.getWidth(), toEdit.getHeight(), Bitmap.Config.ARGB_8888);

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String dateTime = sdf.format(Calendar.getInstance().getTime()); // reading local time in the system

    Canvas cs = new Canvas(dest);
    Paint tPaint = new Paint();
    tPaint.setTextSize(35);
    tPaint.setColor(Color.BLUE);
    tPaint.setStyle(Style.FILL);
    float height = tPaint.measureText("yY");
    cs.drawText(dateTime, 20f, height+15f, tPaint);
    try {
        dest.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/timestamped")));
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    return null;
  }
    return dest;
}

I also recommend not saving it to "/sdcard/" but instead use Environment.getExternalStorageDirectory() (already implemented for you)

Edit: Also noticed you're creating a second Bitmap out of myImageBitmap; is that necessary?

Cruceo
  • 6,763
  • 2
  • 31
  • 52
  • Well once the camera takes the photo and my app gets it from the external storage, i want it to save stamp it, with current data and time and then save it a jpeg. After saved then display the image in a imageview on my main layout am i missing the code for it. Also where would i input your above code into the mix, anywhere specfic. – Jaison Brooks Jul 11 '12 at 23:15
  • Thank you for helping me with, i really appreciate it. Im a beginner developer so some of the terminology and when and where and how to call stuff im still getting over the learning curve. The code above you edited, when you said create method and put that code . what is "that Code" referring too, my bitmap draw text stuff? – Jaison Brooks Jul 11 '12 at 23:23
  • You can just copy the entire method I made and place it anywhere in the class that can access it. To do what you're asking, though, you're going to need a reference to where you're saving the Bitmap (path + name) after it's been saved and then load that into memory and set the ImageView's background attribute programmatically to it – Cruceo Jul 12 '12 at 15:41
  • Thank you for the info on that, do you know of any sample code to get this done. I have attached the code just in the middle of my source, and still no luck with a time stamp coming out of. the intent is working, opens camera, snaps a photo, asked me if i want to save or discard. (i believe part of the camera app) and then takes me back to my main. Doesnt load the image as into imageview, and no timestamp on photo captured. Would it be preferred to load the image into the image view, then like on button click apply the drawtext bitmap code to it. i dont know im still having troubles. – Jaison Brooks Jul 12 '12 at 16:38
  • You could do it that way, but I would get the image, draw the timestamp, load the new Bitmap into the Imageview, and save it as opposed to getting the image, loading it into the ImageView, getting the ImageView's drawing cache, drawing on it, then saving it. You could just return "dest" in the method I provided, and then just load that Bitmap directly into the ImageView. Also, remove cs.drawBitmap() from that, as you're already creating a Canvas on the Bitmap, you don't need to put another Bitmap on top of it (I removed it from post) – Cruceo Jul 12 '12 at 17:12
  • in the code that i have above, Which this code is part of a sample from the developers website. im trying to figure out where to input it, when just pasting the code in i get alert to remove method timestampITandSave() because its not used, i tried adding timestampITandSave() under the "private void handleBigCameraPhoto()" so it looked like private void handleBigCameraPhoto() { if (mCurrentPhotoPath != null) { setPic(); timestampItAndSave(); galleryAddPic(); mCurrentPhotoPath = null; } } is this right? – Jaison Brooks Jul 12 '12 at 17:24
  • Ok, I edited the main post. Inside your setPic() method towards the end, you have: Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); mImageView.setImageBitmap(bitmap); --- Make it: Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); Bitmap replacedBitmap = timestampItAndSave(bitmap); mImageView.setImageBitmap(replacedBitmap); – Cruceo Jul 12 '12 at 17:32
  • Alright, i tried the code in 4.0 emulator and got force close. I have updated my source above with what i attached per your reply. is this properly formated per your recommendations. – Jaison Brooks Jul 12 '12 at 19:28
  • Do you have a email i could contact you directly or email you my source, i do have 3 other java files that are not posted. Actually this is the source i am using, however i have remove the small photo and video buttons/methods and stuff. [http://code.google.com/p/crw-cmu/source/browse/luis/PhotoIntentActivity/src/com/example/android/photobyintent/?r=733] You can download the sample off the developers site too [http://developer.android.com/training/camera/photobasics.html] – Jaison Brooks Jul 12 '12 at 20:32
  • I'm sorry, I've got a lot of projects on my plate at the moment, I don't think I'm going to be able to help you fix this. – Cruceo Jul 12 '12 at 20:34
  • man i really thought we were sooo close to solving this, the image is outputting just time stamp not being applied. sorry to keep bugging you. – Jaison Brooks Jul 12 '12 at 20:38
  • per your request here is my logcat above. sorry to keep buggin you, however you are the only person whom has ever responsed to any of the forums i've posted my source on. – Jaison Brooks Jul 12 '12 at 20:59
  • If i could ask one last thing of you, and i will stop replying to this comments, will you review the log cat above. I believe its may be a simple mistake in my code. im not very good at reading logcat yet, if you could thats be highly appreciate and thank you @Cruceo for all your help with my source, i really appreciate it. – Jaison Brooks Jul 12 '12 at 21:24
  • No problem, sorry I can't help much. But here's your issue: "java.lang.IllegalStateException: Immutable bitmap passed to Canvas constructor" It seems like the Bitmap you're using can't be altered, which means you'll actually have to create a new Bitmap before creating a Canvas on it using the createBitmap() method you had used earlier (which I wrongfully took out). In the timestampItAndSave() method, just create a new Bitmap from the toEdit bitmap you pass in and it should (probably) work – Cruceo Jul 12 '12 at 22:30
  • I have the code that i used for the initial ondraw code that i had, which included this code ` Bitmap dest = Bitmap.createBitmap(mImageBitmap.getWidth(), mImageBitmap.getHeight(), Bitmap.Config.ARGB_8888); then i had my cavas and paint code – Jaison Brooks Jul 12 '12 at 23:18
  • Yeah, "Bitmap dest = Bitmap.createBitmap(toEdit.getWidth(), toEdit.getHeight(), Bitmap.Config.ARGB_8888);" is what you want. Place that at the top of the timestampItAndSave method and then replace the toEdit variables in there with dest. Actually, I'll update the main code... – Cruceo Jul 12 '12 at 23:45
  • Alright, thank you so much. So no force closes anymore, however the photo doesnt have the time stamp, however the imageview shows me a time stamp with no image??? WE are SOOOO close to working this out. do i have something wrong with my dest.compress – Jaison Brooks Jul 12 '12 at 23:53
  • Nope, that was also my fault: I took out your canvas.drawBitmap() call, which you're actually going to have to add again, since you're not actually copying the Bitmap in the above, you're just creating a new one with the same boundaries. As for saving it, your saving method looks correct; is it running into an Exception when trying to compress it? Maybe from the stream still being open by your camera save method? Not sure, but you could take the saving out of there, and then after you've loaded the ImageView, grab its drawing cache and save that – Cruceo Jul 13 '12 at 00:29
  • I updated the code above with what I inputted per your recommendation for the drawbitmap .is this correct. – Jaison Brooks Jul 13 '12 at 01:23
  • Draw the bitmap first, then the text; else the image will be on top of it and you won't see it – Cruceo Jul 13 '12 at 01:37
  • If you have separate questions, please ask new questions. If there's more information you need, please update your question. Comments do not scale. Anything you would like to be kept, please move to either the answer or the question. If you need to communicate in Real Time, use one of our chatrooms, located at: http://chat.stackoverflow.com. – George Stocker Jul 13 '12 at 02:30
  • Cruceo, if you would like to continue to help me out with this, that would so awesome, we are almost there with resolving this and i've already learned alot just from you helping me. If you want to email me @ jaisonbrooks@gmail.com so that we dont rack up these comments. . – Jaison Brooks Jul 13 '12 at 14:52