From 3de6314c72803803135e8d3ce35da128f2572164 Mon Sep 17 00:00:00 2001 From: k3b <1374583+k3b@users.noreply.github.com> Date: Fri, 19 Feb 2021 08:22:50 +0100 Subject: [PATCH] #155: ao10: fixed move photo(s) with autoprocessing and exif edit --- .../k3b/android/androFotoFinder/Global.java | 23 ++++-- .../media/AndroidExifInterfaceEx.java | 75 ++++++++++++------- .../main/java/de/k3b/media/ExifInterface.java | 10 ++- 3 files changed, 75 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/Global.java b/app/src/main/java/de/k3b/android/androFotoFinder/Global.java index d7c3a6d8..7ef2119d 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/Global.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/Global.java @@ -106,20 +106,33 @@ public class Global { ? null : new File(Environment.getExternalStorageDirectory(), "osmdroid"); - /** remember last picked geo-s */ + /** + * remember last picked geo-s + */ public static File pickHistoryFile = null; // initialized in app.onCreate with local database file public static int pickHistoryMax = 25; - /** false: cmd setGeo => form(GeoEditActivity) => mapPicker */ + /** + * false: cmd setGeo => form(GeoEditActivity) => mapPicker + */ public static final boolean geoNoEdit = true; public static final boolean saveXmpAsHumanReadable = true; + /** + * true: cancel exifChange if DatabaseUpdate fails by throwing IOException + */ + public static final boolean cancelExifChangeIfDatabaseUpdateFails = false; + public static class Media { - /** Support extra parameters true: experimental. Not fully implemented yet. */ + /** + * Support extra parameters true: experimental. Not fully implemented yet. + */ public static final boolean enableIptcMediaScanner = true; - /** true: if there is no xmp-file or entry xmp-entry in csv mark this - * SQL_COL_EXT_XMP_LAST_MODIFIED_DATE=EXT_LAST_EXT_SCAN_NO_XMP*. */ + /** + * true: if there is no xmp-file or entry xmp-entry in csv mark this + * SQL_COL_EXT_XMP_LAST_MODIFIED_DATE=EXT_LAST_EXT_SCAN_NO_XMP*. + */ public static final boolean enableXmpNone = enableIptcMediaScanner && true; } diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/media/AndroidExifInterfaceEx.java b/app/src/main/java/de/k3b/android/androFotoFinder/media/AndroidExifInterfaceEx.java index af295183..2721201e 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/media/AndroidExifInterfaceEx.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/media/AndroidExifInterfaceEx.java @@ -45,9 +45,11 @@ public class AndroidExifInterfaceEx extends ExifInterfaceEx { // set to true to log what happens to database-ID when changing exif private static final boolean DBG_RENAME_IN_DB_ENABLED = true; - private boolean overwriteOriginal; - private String inPath; - private String outPath; + /** + * if not null temprary jpg rename is active while edit exif in place (src=dest-file) + */ + private String outTempFilePath; + private String inOriginalFilePath; private Boolean hasXmp; public static void init() { @@ -62,31 +64,31 @@ public ExifInterfaceEx create() { @Override public void saveAttributes(IFile inFile, IFile outFile, boolean deleteInFileOnFinish, Boolean hasXmp) throws IOException { - if (deleteInFileOnFinish) { - renameInDatabase(":saveAttributes", inFile.getCanonicalPath(), outFile.getCanonicalPath(), true); - } else { - if (inFile.equals(outFile)) { - // !!! - // renameSouraceFileBeforeReplaceOrThrow - - } else { + if (!inFile.equals(outFile)) { + // not change exif in place (same file) + if (deleteInFileOnFinish) { // move + renameInDatabase(":saveAttributes", inFile.getCanonicalPath(), outFile.getCanonicalPath(), true, Global.cancelExifChangeIfDatabaseUpdateFails); + } else { // copy insertIntoDatabase(outFile, hasXmp); } - } - //!!! update media database + } // if (!inFile.equals(outFile)) else database update is done later in renameSouraceFileBeforeReplaceOrThrow + super.saveAttributes(inFile, outFile, deleteInFileOnFinish, hasXmp); this.hasXmp = hasXmp; } + /** + * Called for exif change in place (same file is modified with temporary old jpf file). + */ @Override protected IFile renameSouraceFileBeforeReplaceOrThrow(IFile oldSourcefile, String newName) throws IOException { debugIdPaths("renameSouraceFileBeforeReplaceOrThrow begin", oldSourcefile.getAbsolutePath(), newName); - this.overwriteOriginal = true; - this.inPath = oldSourcefile.getAbsolutePath(); - this.outPath = this.inPath + TMP_FILE_SUFFIX; + this.inOriginalFilePath = oldSourcefile.getAbsolutePath(); + this.outTempFilePath = this.inOriginalFilePath + TMP_FILE_SUFFIX; - if (!renameInDatabase(":renameSouraceFileBeforeReplaceOrThrow", this.inPath, this.outPath, false)) { - this.outPath = null; // failed + if (!renameInDatabase(":renameSouraceFileBeforeReplaceOrThrow", + this.inOriginalFilePath, this.outTempFilePath, false, Global.cancelExifChangeIfDatabaseUpdateFails)) { + this.outTempFilePath = null; // failed } final IFile result = super.renameSouraceFileBeforeReplaceOrThrow(oldSourcefile, newName); @@ -95,15 +97,16 @@ protected IFile renameSouraceFileBeforeReplaceOrThrow(IFile oldSourcefile, Strin } @Override - protected void beforeCloseSaveOutputStream() { - if (this.outPath != null) { - renameInDatabase(":beforeCloseSaveOutputStream", this.outPath, this.inPath, true); - this.outPath = null; + protected void beforeCloseSaveOutputStream() throws IOException { + if (this.outTempFilePath != null) { + // rename back temp file after modify exif in place + renameInDatabase(":beforeCloseSaveOutputStream", this.outTempFilePath, this.inOriginalFilePath, true, false); + this.outTempFilePath = null; } super.beforeCloseSaveOutputStream(); } - private void insertIntoDatabase(IFile outFile, Boolean hasXmp) { + private void insertIntoDatabase(IFile outFile, Boolean hasXmp) throws IOException { ContentValues values = new ContentValues(); PhotoPropertiesMediaDBContentValues mediaValueAdapter = new PhotoPropertiesMediaDBContentValues().set(values, null); @@ -121,11 +124,23 @@ private void insertIntoDatabase(IFile outFile, Boolean hasXmp) { values.put(FotoSql.SQL_COL_PATH, outFile.getCanonicalPath()); Uri result = FotoSql.getMediaDBApi().execInsert("Copy with Autoprocessing", values); + if (result != null) { + String message = " copy insertIntoDatabase('" + outFile + "') failed "; + if (DBG_RENAME_IN_DB_ENABLED) { + Log.e(Global.LOG_CONTEXT, message); + } + if (Global.cancelExifChangeIfDatabaseUpdateFails) { + throw new IOException(message); + } + } + } // TODO additional database parameters (see scanner) // DateLastModified, xmpDate, .... - private boolean renameInDatabase(String dbgContext, String fromPath, String toPath, boolean thransferExif) { + private boolean renameInDatabase( + String dbgContext, String fromPath, String toPath, + boolean thransferExif, boolean throwOnErrorMessage) throws IOException { ContentValues values = new ContentValues(); if (thransferExif) { PhotoPropertiesMediaDBContentValues mediaValueAdapter = new PhotoPropertiesMediaDBContentValues().set(values, null); @@ -148,8 +163,16 @@ private boolean renameInDatabase(String dbgContext, String fromPath, String toPa execUpdate(this.getClass().getSimpleName() + dbgContext, fromPath, values, null); debugIdPaths(dbgContext + " renameInDatabase end " + execResultCount, fromPath, toPath); - if ((execResultCount != 1) && DBG_RENAME_IN_DB_ENABLED) { -// !!!! debug ausgabe path+ id failed + if (execResultCount != 1) { + String message = dbgContext + " renameInDatabase('" + fromPath + + "' => '" + toPath + "') returned " + execResultCount + ". Expected 1."; + if (DBG_RENAME_IN_DB_ENABLED) { + Log.e(Global.LOG_CONTEXT, message); + } + if (throwOnErrorMessage) { + this.outTempFilePath = null; + throw new IOException(message); + } } return 1 == execResultCount; } diff --git a/fotolib2/src/main/java/de/k3b/media/ExifInterface.java b/fotolib2/src/main/java/de/k3b/media/ExifInterface.java index a3615c53..5c2ca4a3 100644 --- a/fotolib2/src/main/java/de/k3b/media/ExifInterface.java +++ b/fotolib2/src/main/java/de/k3b/media/ExifInterface.java @@ -1433,11 +1433,14 @@ public void saveAttributes(IFile inFile, IFile outFile, mThumbnailBytes = null; } + /** + * Called for exif change in place (same file is modified with temporary old jpf file). + */ protected IFile renameSouraceFileBeforeReplaceOrThrow(IFile oldSourcefile, String newName) throws IOException { logDebug(String.format("rename %s to %s", oldSourcefile, newName)); if (!oldSourcefile.renameTo(newName)) { - throw new IOException("Could'nt rename sourcefile from " + oldSourcefile + + throw new IOException("Filesystem: Could'nt rename sourcefile from " + oldSourcefile + " to " + newName); } return oldSourcefile.getParentFile().create(newName); @@ -1913,7 +1916,10 @@ public void saveJpegAttributes(InputStream inputStream, OutputStream outputStrea } } - protected void beforeCloseSaveOutputStream() { + /** + * Hook for android for file post-processing + */ + protected void beforeCloseSaveOutputStream() throws IOException { } // Reads the given EXIF byte area and save its tag data into attributes.