Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#169: Update exif-file-info also updates mediadb without changing med…
Browse files Browse the repository at this point in the history
…iadb-id
k3b committed Dec 10, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 1f51e1c commit 461b889
Showing 11 changed files with 266 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -4,21 +4,29 @@
import android.util.Log;

import java.io.IOException;
import java.util.Date;

import de.k3b.android.androFotoFinder.Global;
import de.k3b.android.androFotoFinder.queries.FotoSql;
import de.k3b.android.androFotoFinder.tagDB.TagSql;
import de.k3b.io.FileUtils;
import de.k3b.io.VISIBILITY;
import de.k3b.io.collections.SelectedFiles;
import de.k3b.io.filefacade.IFile;
import de.k3b.media.ExifInterfaceEx;
import de.k3b.media.PhotoPropertiesUtil;

/**
* Android specific Version of {@link ExifInterfaceEx} that updates the
* Database, when saving exif changes.
*/
public class AndroidExifInterfaceEx extends ExifInterfaceEx {
public static boolean DBG_ENABLED = true;

private boolean overwriteOriginal;
private String inPath;
private String outPath;
private Boolean hasXmp;

public static void init() {
setFactory(new Factory() {
@@ -29,14 +37,21 @@ public ExifInterfaceEx create() {
});
}

@Override
public void saveAttributes(IFile inFile, IFile outFile,
boolean deleteInFileOnFinish, Boolean hasXmp) throws IOException {
super.saveAttributes(inFile, outFile, deleteInFileOnFinish, hasXmp);
this.hasXmp = hasXmp;
}

@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;

if (!renameInDatabase(":renameSouraceFileBeforeReplaceOrThrow", this.inPath, this.outPath)) {
if (!renameInDatabase(":renameSouraceFileBeforeReplaceOrThrow", this.inPath, this.outPath, false)) {
this.outPath = null; // failed
}

@@ -48,16 +63,33 @@ protected IFile renameSouraceFileBeforeReplaceOrThrow(IFile oldSourcefile, Strin
@Override
protected void beforeCloseSaveOutputStream() {
if (this.outPath != null) {
renameInDatabase(":beforeCloseSaveOutputStream", this.outPath, this.inPath);
renameInDatabase(":beforeCloseSaveOutputStream", this.outPath, this.inPath, true);
this.outPath = null;
}
super.beforeCloseSaveOutputStream();
}

private boolean renameInDatabase(String dbgContext, String fromPath, String toPath) {
debugIdPaths(dbgContext + " renameInDatabase begin", fromPath, toPath);
// TODO additional database parameters (see scanner)
// DateLastModified, xmpDate, ....
private boolean renameInDatabase(String dbgContext, String fromPath, String toPath, boolean thransferExif) {
ContentValues values = new ContentValues();
if (thransferExif) {
PhotoPropertiesMediaDBContentValues mediaValueAdapter = new PhotoPropertiesMediaDBContentValues().set(values, null);

PhotoPropertiesUtil.copyNonEmpty(mediaValueAdapter, this);

Date lastModified = new Date();
TagSql.setFileModifyDate(values, lastModified);
if (this.hasXmp != null) {
if (this.hasXmp) {
TagSql.setXmpFileModifyDate(values, lastModified);
} else {
TagSql.setXmpFileModifyDate(values, TagSql.EXT_LAST_EXT_SCAN_NO_XMP);
}
}
}
values.put(FotoSql.SQL_COL_PATH, toPath);
debugIdPaths(dbgContext + " renameInDatabase begin", fromPath, toPath);
final int execResultCount = FotoSql.getMediaDBApi().
execUpdate(this.getClass().getSimpleName() + dbgContext, fromPath, values, null);

Original file line number Diff line number Diff line change
@@ -204,7 +204,7 @@ private void updateDB(String dbgContext, String _path, long xmlLastFileModifyDat
TagSql.setXmpFileModifyDate(dbValues, xmlLastFileModifyDate);
}

TagSql.setFileModifyDate(dbValues, new Date().getTime() / 1000);
TagSql.setFileModifyDate(dbValues, new Date());

mUpdateCount += TagSql.execUpdate(dbgContext, path, xmlLastFileModifyDate, dbValues, VISIBILITY.PRIVATE_PUBLIC);
mItemCount++;
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ public void clearMediaCopy() {
public void rebuild(Context context, IProgessListener progessListener) {
long start = new Date().getTime();
clearMediaCopy();
MediaDBRepository.Impl.updateMedaiCopy(context, writableDatabase, null, progessListener);
MediaDBRepository.Impl.updateMediaCopy(context, writableDatabase, null, progessListener);
start = (new Date().getTime() - start) / 1000;
final String text = "load db " + start + " secs";
Toast.makeText(context, text, Toast.LENGTH_LONG).show();
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ public class MediaDBRepository implements IMediaRepositoryApi {
// #155
public static final boolean debugEnabledSqlRefresh = true;

private static final String MODUL_NAME = MediaContentproviderRepositoryImpl.class.getName();
private static final String MODUL_NAME = MediaDBRepository.class.getSimpleName();
private static String currentUpdateReason = null;
private static long currentUpdateId = 1;
private static int transactionNumber = 0;
@@ -360,6 +360,41 @@ public void endTransaction() {
db.endTransaction();
}

/**
* generic method to get values from current MediaDBApi-Implementation
*
* @param fullFilePathFilter
* @param destination
* @param dbgContext
* @return
*/
public static ContentValues getContentValues(String fullFilePathFilter, ContentValues destination, String dbgContext) {
final String meldung = MODUL_NAME + ".getContentValues(" + dbgContext + "," + fullFilePathFilter + ")";
QueryParameter query = new QueryParameter().addColumn(MediaDBRepository.Impl.USED_MEDIA_COLUMNS);
query.removeFirstColumnThatContains(FotoSql.SQL_COL_PK);
FotoSql.setWhereFileNames(query, fullFilePathFilter);

Cursor c = null;

try {
c = FotoSql.getMediaDBApi().createCursorForQuery(null, meldung, query, null, null);
if (c.moveToNext()) {
if (destination == null) {
destination = new ContentValues();
}
return Impl.getContentValues(c, destination);
}
} catch (Exception ex) {
Log.e(LOG_TAG, meldung +
" error :", ex);
} finally {
if (c != null) c.close();
}

return null;
}


public static class Impl {
/**
* SQL to create copy of contentprovider MediaStore.Images.
@@ -561,7 +596,7 @@ public static void clearMedaiCopy(SQLiteDatabase db) {
}


public static int updateMedaiCopy(Context context, SQLiteDatabase db, Date lastUpdate, IProgessListener progessListener) {
public static int updateMediaCopy(Context context, SQLiteDatabase db, Date lastUpdate, IProgessListener progessListener) {
int progress = 0;
java.util.Date startTime = new java.util.Date();

Original file line number Diff line number Diff line change
@@ -318,6 +318,12 @@ public static void setFileModifyDate(ContentValues values, String path) {
}
}

public static void setFileModifyDate(ContentValues values, Date fileModifyDate) {
if (fileModifyDate != null) {
setFileModifyDate(values, fileModifyDate.getTime() / 1000);
}
}

public static void setFileModifyDate(ContentValues values, long fileModifyDateSecs) {
if (fileModifyDateSecs != 0) {
values.put(SQL_COL_LAST_MODIFIED, fileModifyDateSecs);
5 changes: 2 additions & 3 deletions app/src/main/java/de/k3b/android/io/AndroidFileCommands.java
Original file line number Diff line number Diff line change
@@ -61,7 +61,6 @@
import de.k3b.android.widget.FilePermissionActivity;
import de.k3b.database.QueryParameter;
import de.k3b.io.DirectoryFormatter;
import de.k3b.io.FileCommands;
import de.k3b.io.FileUtils;
import de.k3b.io.IDirectory;
import de.k3b.io.IProgessListener;
@@ -78,10 +77,10 @@
/**
* Api to manipulate files/photos.
* Same as FileCommands with update media database.
*
* <p>
* Created by k3b on 03.08.2015.
*/
public class AndroidFileCommands extends FileCommands {
public class AndroidFileCommands extends AndroidFileCommandsDbImpl {
private static final String SETTINGS_KEY_LAST_COPY_TO_PATH = "last_copy_to_path";
private static final String mDebugPrefix = "AndroidFileCommands.";
private boolean isInBackground = false;
138 changes: 138 additions & 0 deletions app/src/main/java/de/k3b/android/io/AndroidFileCommandsDbImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright (c) 2015-2020 by k3b.
*
* This file is part of AndroFotoFinder / #APhotoManager.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>
*/

package de.k3b.android.io;

import android.content.ContentValues;
import android.net.Uri;

import java.io.File;

import de.k3b.android.androFotoFinder.queries.FotoSql;
import de.k3b.android.androFotoFinder.queries.MediaDBRepository;
import de.k3b.io.FileCommands;
import de.k3b.io.filefacade.IFile;
import de.k3b.media.PhotoPropertiesUtil;

/**
* Api to manipulate files/photos.
* Same as FileCommands with update media database.
* <p>
* Created by k3b on 03.08.2015.
*/
public class AndroidFileCommandsDbImpl extends FileCommands {
/**
* copies a file from the sourceFullPath path to the target path.
* Android specific: also updates database.
*
* @param sourceFullPath the path of the file that shall be copied including the file name with ending
* @param targetFullPath the path of the file that shall be written to with filename
*/
@Override
protected boolean osFileCopy(IFile targetFullPath, IFile sourceFullPath) {
//!!! TODO muss noch getestet werden
final String srcPath = sourceFullPath.getAbsolutePath();
String toPath = null;
boolean dbSuccess = false;
if (PhotoPropertiesUtil.isImage(srcPath, PhotoPropertiesUtil.IMG_TYPE_ALL)) {
toPath = new File(targetFullPath.getFile(), targetFullPath.getName()).getAbsolutePath();
dbSuccess = (null != copyInDatabase("osFileCopy", srcPath, toPath));
}

if (dbSuccess) {
dbSuccess = super.osFileCopy(targetFullPath, sourceFullPath);
}
return dbSuccess;
}

/**
* Moves a file from the sourceFullPath path to the target path.
* Android specific: also updates database.
*
* @param sourceFullPath the path of the file that shall be copied including the file name with ending
* @param targetFullPath the path of the file that shall be written to with filename
*/
@Override
protected boolean osFileMove(IFile targetFullPath, IFile sourceFullPath) {
final String srcPath = sourceFullPath.getAbsolutePath();
String toPath = null;
boolean dbSuccess = false;
// Database update must be done before super.osFileMove to avoid deleting/recreating old file entry
if (PhotoPropertiesUtil.isImage(srcPath, PhotoPropertiesUtil.IMG_TYPE_ALL)) {
toPath = new File(targetFullPath.getFile(), targetFullPath.getName()).getAbsolutePath();
dbSuccess = renameInDatabase("osFileMove", srcPath, toPath);
}
final boolean osSuccess = super.osFileMove(targetFullPath, sourceFullPath);
if (!osSuccess && dbSuccess) {
// os falled. Rollback
renameInDatabase("osFileMove-rollback", toPath, srcPath);
}
return osSuccess;
}

/**
* Renames a file from the sourceFullPath path to the target path.
* Android specific: also updates database.
*
* @param sourceFullPath the path of the file that shall be copied including the file name with ending
* @param targetFullPath the path of the file that shall be written to with filename
*/
@Override
protected boolean osRenameTo(IFile targetFullPath, IFile sourceFullPath) {
final String srcPath = sourceFullPath.getAbsolutePath();
String toPath = null;
boolean dbSuccess = false;
if (PhotoPropertiesUtil.isImage(srcPath, PhotoPropertiesUtil.IMG_TYPE_ALL)) {
toPath = targetFullPath.getAbsolutePath();
dbSuccess = renameInDatabase("osRenameTo", srcPath, toPath);
}
final boolean osSuccess = super.osRenameTo(targetFullPath, sourceFullPath);
if (!osSuccess && dbSuccess) {
// os falled. Rollback
renameInDatabase("osRenameTo-rollback", toPath, srcPath);
}
return osSuccess;
}

@Override
protected boolean osDeleteFile(IFile file) {
return super.osDeleteFile(file);
}

private boolean renameInDatabase(String dbgContext, String fromPath, String toPath) {
ContentValues values = new ContentValues();
values.put(FotoSql.SQL_COL_PATH, toPath);
final int execResultCount = FotoSql.getMediaDBApi().
execUpdate(this.getClass().getSimpleName() + dbgContext, fromPath, values, null);

return 1 == execResultCount;
}

private Uri copyInDatabase(String dbgContext, String fromPath, String toPath) {
ContentValues values = MediaDBRepository.getContentValues(fromPath, null, dbgContext);
if (values != null) {
values.put(FotoSql.SQL_COL_PATH, toPath);
return FotoSql.getMediaDBApi().
execInsert(this.getClass().getSimpleName() + dbgContext, values);
}
return null;
}

}

62 changes: 33 additions & 29 deletions fotolib2/src/main/java/de/k3b/io/FileCommands.java
Original file line number Diff line number Diff line change
@@ -127,11 +127,10 @@ protected boolean canProcessFile(int opCode) {
}

/**
*
* @param sourceFullPath the path of the file that shall be copied including the file name with ending
* @param targetFullPath the path of the file that shall be written to without filename
*
* Copies a file from the sourceFullPath path to the target path.
* @param targetFullPath the path of the file that shall be written to with filename
* <p>
* Copies a file from the sourceFullPath path to the target path.
*/
private static boolean _osFileCopy(IFile targetFullPath, IFile sourceFullPath, FileCommands owner) {
boolean result = true;
@@ -454,22 +453,25 @@ protected int moveOrCopyFiles(final boolean move, String what, PhotoPropertiesDi

/**
* executes os specific move or copy operation and updates the list of modified files
*
* @param sourceFullPath the path of the file that shall be copied including the file name with ending
* @param targetFullPath the path of the file that shall be written to with filename
*/
protected boolean osFileMoveOrCopy(boolean move, IFile dest, IFile source) {
protected boolean osFileMoveOrCopy(boolean move, IFile targetFullPath, IFile sourceFullPath) {
boolean result = false;
long fileTime = source.lastModified();
long fileTime = sourceFullPath.lastModified();

if (move) {
result = osFileMove(dest, source);
result = osFileMove(targetFullPath, sourceFullPath);
} else {
result = osFileCopy(dest, source);
result = osFileCopy(targetFullPath, sourceFullPath);
}
if (dest.lastModified() != fileTime) {
dest.setLastModified(fileTime);
if (targetFullPath.lastModified() != fileTime) {
targetFullPath.setLastModified(fileTime);
}

if (result) {
addProcessedFiles(move, dest, source);
addProcessedFiles(move, targetFullPath, sourceFullPath);
}

return result;
@@ -484,35 +486,38 @@ private void addProcessedFiles(boolean move, IFile dest, IFile source) {

/**
* can be replaced by mock/stub in unittests
*
* @param sourceFullPath the path of the file that shall be copied including the file name with ending
* @param targetFullPath the path of the file that shall be written to with filename
*/
protected boolean osFileMove(IFile dest, IFile source) {
if (osRenameTo(dest, source)) {
protected boolean osFileMove(IFile targetFullPath, IFile sourceFullPath) {
if (osRenameTo(targetFullPath, sourceFullPath)) {
// move within same mountpoint
if (LibGlobal.debugEnabledJpg) {
logger.info("osFileMove(rename) '" + source
+ "' => '" + dest + "'");
logger.info("osFileMove(rename) '" + sourceFullPath
+ "' => '" + targetFullPath + "'");
}
return true;
}

// #61 cannot move between different mountpoints/devices/partitions. do Copy+Delete instead
if (osFileExists(source) && source.isFile() && source.canRead()
&& source.canWrite() // to delete after success
&& !osFileExists(dest)
&& osFileCopy(dest, source)) {
if (osDeleteFile(source)) {
if (osFileExists(sourceFullPath) && sourceFullPath.isFile() && sourceFullPath.canRead()
&& sourceFullPath.canWrite() // to delete after success
&& !osFileExists(targetFullPath)
&& osFileCopy(targetFullPath, sourceFullPath)) {
if (osDeleteFile(sourceFullPath)) {
if (LibGlobal.debugEnabledJpg) {
logger.info("osFileMove(copy+delete) '" + source
+ "' => '" + dest + "'");
logger.info("osFileMove(copy+delete) '" + sourceFullPath
+ "' => '" + targetFullPath + "'");
}
return true; // move: copy + delete(source) : success
return true; // move: copy + delete(sourceFullPath) : success
} else {
// cannot delete souce: undo copy
if (LibGlobal.debugEnabledJpg) {
logger.info("osFileMove failed for '" + source
+ "' => '" + dest + "'");
logger.info("osFileMove failed for '" + sourceFullPath
+ "' => '" + targetFullPath + "'");
}
osDeleteFile(dest);
osDeleteFile(targetFullPath);
}
}
return false;
@@ -530,11 +535,10 @@ protected boolean osRenameTo(IFile dest, IFile source) {
}

/**
* Copies a file from the sourceFullPath path to the target path.
*
* @param sourceFullPath the path of the file that shall be copied including the file name with ending
* @param targetFullPath the path of the file that shall be written to without filename
*
* Copies a file from the sourceFullPath path to the target path.
* @param targetFullPath the path of the file that shall be written to with filename
*/
protected boolean osFileCopy(IFile targetFullPath, IFile sourceFullPath) {
return _osFileCopy(targetFullPath, sourceFullPath, this);
13 changes: 8 additions & 5 deletions fotolib2/src/main/java/de/k3b/media/ExifInterface.java
Original file line number Diff line number Diff line change
@@ -1174,20 +1174,23 @@ public String getDebugString(String lineDelimiter, String... _keysToExclude) {
* and make a single call rather than multiple calls for each attribute.
*/
public void saveAttributes() throws IOException {
saveAttributes(mExifFile, mExifFile, true);
saveAttributes(mExifFile, mExifFile, true, null);
}

/**
* Old File based implementation.
*
* @deprecated use {@link #saveAttributes(IFile, IFile, boolean)} instead
* @deprecated use {@link #saveAttributes(IFile, IFile, boolean, Boolean)} instead
*/
@Deprecated
public void saveAttributes(File inFile, File outFile, boolean deleteInFileOnFinish) throws IOException {
saveAttributes(FileFacade.convert("ExifInterface.saveAttributes in", inFile), FileFacade.convert("ExifInterface.saveAttributes out", outFile), deleteInFileOnFinish);
saveAttributes(FileFacade.convert("ExifInterface.saveAttributes in", inFile),
FileFacade.convert("ExifInterface.saveAttributes out", outFile),
deleteInFileOnFinish, null);
}

public void saveAttributes(IFile inFile, IFile outFile, boolean deleteInFileOnFinish) throws IOException {
public void saveAttributes(IFile inFile, IFile outFile,
boolean deleteInFileOnFinish, Boolean hasXmp) throws IOException {
String debugContext = String.format("%s.saveAttributes(%s=>%s,deleteInFileOnFinish=%s)", this.getClass().getSimpleName(), inFile, outFile, deleteInFileOnFinish);
IFile currentOutFile = outFile;
fixAttributes();
@@ -1284,7 +1287,7 @@ public byte[] getThumbnail() {
}

/**
* @deprecated use {@link #saveAttributes(IFile, IFile, boolean)} instead
* @deprecated use {@link #getThumbnail(IFile)} instead
*/
@Deprecated
public byte[] getThumbnail(File inFile) {
4 changes: 2 additions & 2 deletions fotolib2/src/main/java/de/k3b/media/ExifInterfaceEx.java
Original file line number Diff line number Diff line change
@@ -169,9 +169,9 @@ public static int getOrientationId(IFile fullPath) {
}

@Override
public void saveAttributes(IFile inFile, IFile outFile, boolean deleteInFileOnFinish) throws IOException {
public void saveAttributes(IFile inFile, IFile outFile, boolean deleteInFileOnFinish, Boolean hasXmp) throws IOException {
fixDateTakenIfNeccessary(inFile);
super.saveAttributes(inFile, outFile, deleteInFileOnFinish);
super.saveAttributes(inFile, outFile, deleteInFileOnFinish, hasXmp);
setFilelastModified(outFile);
}

Original file line number Diff line number Diff line change
@@ -278,7 +278,8 @@ private int transferExif(String dbg_context) throws IOException {
exif.saveAttributes(
inJpgFullPath,
outJpgFullPath,
this.deleteOriginalAfterFinish);
this.deleteOriginalAfterFinish,
this.xmp != null);
} else if (!isSameFile) {
// changes are NOT written to exif. Do File copy instead.
FileUtils.copyReplace(inJpgFullPath, outJpgFullPath, this.deleteOriginalAfterFinish, dbg_context + "-transferExif");

0 comments on commit 461b889

Please sign in to comment.