Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imageCaptureProcessing maintain EXIF #2902

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ private static Pair<File, String> moveAndScaleImage(File originalImage, boolean
if (currentWidget != null) {
int maxDimen = currentWidget.getMaxDimen();
if (maxDimen != -1) {
savedScaledImage = FileUtil.scaleAndSaveImage(originalImage, tempFilePathForScaledImage, maxDimen);
File tempFile = new File(tempFilePathForScaledImage);
savedScaledImage = FileUtil.scaleAndSaveImageWithExif(originalImage, tempFile, maxDimen);
}
}
}
Expand Down Expand Up @@ -92,7 +93,7 @@ private static File makeRawCopy(File originalImage, String instanceFolder, Strin
}
File rawImageFile = new File(rawDirPath + "/" + imageFilename);
try {
FileUtil.copyFile(originalImage, rawImageFile);
FileUtil.copyFileWithExifData(originalImage, rawImageFile);
} catch (Exception e) {
throw new IOException("Failed to rename " + originalImage.getAbsolutePath() +
" to " + rawImageFile.getAbsolutePath());
Expand Down
96 changes: 90 additions & 6 deletions app/src/org/commcare/utils/FileUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.media.ExifInterface;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
Expand Down Expand Up @@ -397,7 +398,6 @@ public static void ensureFilePathExists(File f) {
* if we are on KitKat we need use the new API to find the mounted roots, then append our application
* specific path that we're allowed to write to
*/
@SuppressLint("NewApi")
private static String getExternalDirectoryKitKat(Context c) {
File[] extMounts = c.getExternalFilesDirs(null);
// first entry is emualted storage. Second if it exists is secondary (real) SD.
Expand Down Expand Up @@ -494,8 +494,9 @@ public static String getMd5Hash(File file) {

BigInteger number = new BigInteger(1, messageDigest);
String md5 = number.toString(16);
while (md5.length() < 32)
while (md5.length() < 32) {
md5 = "0" + md5;
}
is.close();
return md5;

Expand Down Expand Up @@ -612,7 +613,8 @@ public static boolean scaleAndSaveImage(File originalImage, String finalFilePath
Pair<Bitmap, Boolean> bitmapAndScaledBool = MediaUtil.inflateImageSafe(originalImage.getAbsolutePath());
if (bitmapAndScaledBool.second) {
Logger.log(LogTypes.TYPE_FORM_ENTRY,
"An image captured during form entry was too large to be processed at its original size, and had to be downsized");
"An image captured during form entry was too large to be processed at its original size, " +
"and had to be downsized");
}
Bitmap scaledBitmap = getBitmapScaledByMaxDimen(bitmapAndScaledBool.first, maxDimen);
if (scaledBitmap != null) {
Expand Down Expand Up @@ -723,7 +725,9 @@ public static Uri getUriForExternalFile(Context context, File file) {
* @param dstFile destination File where we need to copy the inputStream
*/
public static void copyFile(InputStream inputStream, File dstFile) throws IOException {
if (inputStream == null) return;
if (inputStream == null) {
return;
}
melmathari marked this conversation as resolved.
Show resolved Hide resolved
OutputStream outputStream = new FileOutputStream(dstFile);
StreamsUtil.writeFromInputToOutputUnmanaged(inputStream, outputStream);
inputStream.close();
Expand Down Expand Up @@ -844,8 +848,13 @@ public static void addMediaToGallery(Context context, File file) throws
}

/**
* Returns true only when we're certain that the file size is too large.
* <p> https://developer.android.com/training/secure-file-sharing/retrieve-info.html#RetrieveFileInfo
* Checks if a file referenced by a content URI exceeds the maximum allowed upload size
* <p>
* @param contentResolver ContentResolver to query the file size
* @param uri Content URI of the file to check
* @return true if the file size exceeds FormUploadUtil.MAX_BYTES, false otherwise or if size cannot be determined
* @see FormUploadUtil#MAX_BYTES
* @see <a href="https://developer.android.com/training/secure-file-sharing/retrieve-info.html#RetrieveFileInfo">Android docs</a>
melmathari marked this conversation as resolved.
Show resolved Hide resolved
*/
public static boolean isFileTooLargeToUpload(ContentResolver contentResolver, Uri uri) {
try (Cursor returnCursor = contentResolver.query(uri, null, null, null, null)) {
Expand All @@ -857,4 +866,79 @@ public static boolean isFileTooLargeToUpload(ContentResolver contentResolver, Ur
return returnCursor.getLong(sizeIndex) > FormUploadUtil.MAX_BYTES;
}
}

shubham1g5 marked this conversation as resolved.
Show resolved Hide resolved
public static void copyFileWithExifData(File sourceFile, File destFile) throws IOException {
// First copy the file normally
copyFile(sourceFile, destFile);

// Only try to copy EXIF data for image files
String mimeType = getMimeType(sourceFile.getAbsolutePath());
if (mimeType != null && mimeType.startsWith("image/")) {
try {
copyExifData(sourceFile.getAbsolutePath(), destFile.getAbsolutePath());
} catch (Exception e) {
Logger.log(LogTypes.TYPE_WARNING_NETWORK,
"Failed to copy EXIF data: " + e.getMessage());
}
melmathari marked this conversation as resolved.
Show resolved Hide resolved
}
}

private static void copyExifData(String sourcePath, String destPath) throws IOException {
try {
ExifInterface source = new ExifInterface(sourcePath);
ExifInterface dest = new ExifInterface(destPath);

String[] tagsToPreserve = {
// GPS data
ExifInterface.TAG_GPS_LATITUDE,
ExifInterface.TAG_GPS_LATITUDE_REF,
ExifInterface.TAG_GPS_LONGITUDE,
ExifInterface.TAG_GPS_LONGITUDE_REF,
ExifInterface.TAG_GPS_TIMESTAMP,
ExifInterface.TAG_GPS_DATESTAMP,
ExifInterface.TAG_GPS_ALTITUDE,
ExifInterface.TAG_GPS_ALTITUDE_REF,
ExifInterface.TAG_GPS_AREA_INFORMATION,

// Timestamp data
ExifInterface.TAG_DATETIME,
ExifInterface.TAG_DATETIME_DIGITIZED,
ExifInterface.TAG_DATETIME_ORIGINAL,
ExifInterface.TAG_OFFSET_TIME,
ExifInterface.TAG_OFFSET_TIME_ORIGINAL,
ExifInterface.TAG_OFFSET_TIME_DIGITIZED,

// Image metadata
ExifInterface.TAG_COPYRIGHT,
ExifInterface.TAG_IMAGE_DESCRIPTION,
ExifInterface.TAG_EXIF_VERSION,
ExifInterface.TAG_ORIENTATION
};

for (String tag : tagsToPreserve) {
String value = source.getAttribute(tag);
if (value != null) {
dest.setAttribute(tag, value);
}
}

dest.saveAttributes();
} catch (IOException e) {
// Log but don't fail if EXIF copying fails
Logger.log(LogTypes.TYPE_WARNING_NETWORK,
"Failed to copy EXIF data from " + sourcePath + " to " + destPath + ": " + e.getMessage());
}
}

public static boolean scaleAndSaveImageWithExif(File sourceFile, File destFile, int maxDimen) throws IOException {
// First scale the image
boolean scaled = scaleAndSaveImage(sourceFile, destFile.getAbsolutePath(), maxDimen);

if (scaled) {
// Copy EXIF data from source to scaled image
copyExifData(sourceFile.getAbsolutePath(), destFile.getAbsolutePath());
}

return scaled;
}
melmathari marked this conversation as resolved.
Show resolved Hide resolved
}