Reputation: 5826
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;
}
Upvotes: 3
Views: 8137
Reputation: 6824
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?
Upvotes: 6