This is definitive fix for the Android's problem of duplicating a photo. Tested it on 2.3 (which has the bug), 4.x (which doesnt have the bug) and 5.x (which has the bug too).
The problem is that the photo is saved in two formats: usually one is the timestamp.jpg, and the other one is the full_date_and_time.jpg; sometimes in the same folder, sometimes in different folders. For example, "1430910805600.jpg" and "2015-05-06 11.14.00.jpg". Worstly, one cannot be converted to the other.
The fix uses code that i found in some questions here in StackOverflow, and also an improvement of my own.
First, you get the image with the URI you used to create the intent, get its added date, and delete it. Then, you get the last image added to the gallery, and compare with the added date of the original image. I delete it if difference is less than one second (its usually less than 10ms).
This is the code used to take the photo:
private static final int EXTCAMERA_RETURN = 1234324334;
private String imageFN; // stored globally but could be a parameter
...
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "tctemp.jpg");
values.put (MediaStore.Images.Media.IS_PRIVATE, 1);
capturedImageURI = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, capturedImageURI);
startActivityForResult(intent, EXTCAMERA_RETURN);
And this is the one to grab the taken photo:
String[] projection = {MediaStore.Images.Media.DATA, BaseColumns._ID, MediaStore.Images.Media.DATE_ADDED};
Cursor cursor = managedQuery(capturedImageURI, projection, null, null, null);
cursor.moveToFirst();
String capturedImageFilePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
long date = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_ADDED));
if (capturedImageFilePath == null || !AndroidUtils.copyFile(capturedImageFilePath,imageFN,cameraType == CAMERA_NATIVE_NOCOPY))
resultCode = RESULT_OK+1; // error
else
{
autoRotatePhoto(imageFN);
getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, BaseColumns._ID + "=" + cursor.getString(cursor.getColumnIndexOrThrow(BaseColumns._ID)), null);
try {new File(capturedImageFilePath).delete();} catch (Exception e) {} // on android 2.3 the code above does not work, so we just ensure that we delete the file
removeLastImageFromGallery(date);
}
This code is used to remove the last photo of the gallery IF the difference is less than one second
private void removeLastImageFromGallery(long orig)
{
try
{
final String[] imageColumns = { MediaStore.Images.Media._ID, MediaStore.Images.Media.DATE_ADDED };
final String imageOrderBy = MediaStore.Images.Media._ID+" DESC";
Cursor imageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageColumns, null, null, imageOrderBy);
if (imageCursor.moveToFirst())
{
long last = imageCursor.getLong(imageCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_ADDED));
int id = imageCursor.getInt(imageCursor.getColumnIndex(MediaStore.Images.Media._ID));
long dif = Math.abs(orig-last);
if (dif < 1) // 1 second - usually is less than 10ms
getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media._ID + "=?", new String[]{ Long.toString(id) } );
}
}
catch (Exception e)
{
AndroidUtils.handleException(e, false);
}
}
Finally, the autorotate code is:
public static void autoRotatePhoto(String imagePath)
{
try
{
File f = new File(imagePath);
ExifInterface exif = new ExifInterface(f.getPath());
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
AndroidUtils.debug(imagePath+" -> "+orientation);
int angle = 0;
switch (orientation)
{
case ExifInterface.ORIENTATION_ROTATE_90: angle = 90; break;
case ExifInterface.ORIENTATION_ROTATE_180: angle = 180; break;
case ExifInterface.ORIENTATION_ROTATE_270: angle = 270; break;
default: return;
}
Matrix mat = new Matrix();
mat.postRotate(angle);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, options);
Bitmap bitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true);
FileOutputStream out = new FileOutputStream(f);
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, out);
out.close();
AndroidUtils.debug("auto-rotated "+imagePath);
}
catch (Exception e)
{
AndroidUtils.handleException(e, false);
}
}
The AndroidUtils.debug and handleException are used to dump the Log and print the exception. The copyFile is shown here:
public static boolean copyFile(String in, String out, boolean deleteOriginal)
{
try
{
byte[] buf = new byte[4096];
FileInputStream fin = new FileInputStream(in);
FileOutputStream fout = new FileOutputStream(out);
int r;
while ((r=fin.read(buf,0,buf.length)) > 0)
fout.write(buf,0,r);
fin.close();
fout.close();
if (deleteOriginal)
new File(in).delete();
return true;
}
catch (Exception e)
{
handleException(e,false);
return false;
}
}