Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 155a8fb1500a240323fb327e16bc38901e7c7731
Author: k3b <[email protected]>
Date:   Sat Dec 12 20:11:58 2020 +0100

    #169: Update exif-file-info also updates mediadb without changing mediadb-id

commit 04cef1a
Author: k3b <[email protected]>
Date:   Fri Dec 11 12:58:29 2020 +0100

    #155: Fix crash if missing file permissions

commit 461b889
Author: k3b <[email protected]>
Date:   Thu Dec 10 20:27:05 2020 +0100

    #169: Update exif-file-info also updates mediadb without changing mediadb-id

commit 1f51e1c
Author: k3b <[email protected]>
Date:   Mon Sep 28 19:54:02 2020 +0200

    #169: Experiment to trace file rename
  • Loading branch information
k3b committed Dec 12, 2020
1 parent 1c6f767 commit feaa790
Show file tree
Hide file tree
Showing 17 changed files with 489 additions and 99 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ android {

minSdkVersion 14 // Android 4.0 Ice Cream Sandwich (API 14); Android 4.4 KitKat (API 19); Android 5.0 Lollipop (API 21);
// Android 6.0 Marshmallow (API 23); Android 7.0 Nougat (API 24)
maxSdkVersion 28 // #155: android-10=api29
// maxSdkVersion 28 // #155: android-10=api29

targetSdkVersion 28

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import de.k3b.android.androFotoFinder.queries.MediaContentproviderRepository;
import de.k3b.android.androFotoFinder.queries.MediaContentproviderRepositoryImpl;
import de.k3b.android.androFotoFinder.queries.MediaDBRepository;
import de.k3b.android.androFotoFinder.queries.MediaRepositoryApiWrapper;
import de.k3b.android.androFotoFinder.queries.MergedMediaRepository;
import de.k3b.android.io.AndroidFileFacade;
import de.k3b.android.io.DocumentFileTranslator;
Expand Down Expand Up @@ -106,31 +107,77 @@ public static void setMediaImageDbReplacement(Context context, boolean useMediaI

Global.useAo10MediaImageDbReplacement = useMediaImageDbReplacement;

final MediaContentproviderRepository mediaContentproviderRepository = new MediaContentproviderRepository(context);

if (Global.useAo10MediaImageDbReplacement) {
final SQLiteDatabase writableDatabase = DatabaseHelper.getWritableDatabase(context);
final MediaDBRepository mediaDBRepository = new MediaDBRepository(writableDatabase);
FotoSql.setMediaDBApi(new MergedMediaRepository(mediaDBRepository, mediaContentproviderRepository));
registerAo10MediaImageDbReplacement(context);
} else {
registerMediaContentProvider(context, oldMediaDBApi);
}
}
}

MediaContent2DBUpdateService.instance = new MediaContent2DBUpdateService(context, writableDatabase);
private static void registerMediaContentProvider(Context context, IMediaRepositoryApi oldMediaDBApi) {
final MediaContentproviderRepository mediaContentproviderRepository = new MediaContentproviderRepository(context);
PhotoChangeNotifyer.unregisterContentObserver(context, GlobalMediaContentObserver.getInstance(context));
if ((oldMediaDBApi != null) && (MediaContent2DBUpdateService.instance != null)) {
// switching from mediaImageDbReplacement to Contentprovider
MediaContent2DBUpdateService.instance.clearMediaCopy();
}
FotoSql.setMediaDBApi(mediaContentproviderRepository);
MediaContent2DBUpdateService.instance = null;
}

if (FotoSql.getCount(new QueryParameter().addWhere("1 = 1")) == 0) {
// database is empty; reload from Contentprovider
MediaContent2DBUpdateService.instance.rebuild(context, null);
}
/**
* Android-10-ff use copy of media database for reading to circumvent android-10-media-contentprovider-restrictions
*/
private static IMediaRepositoryApi registerAo10MediaImageDbReplacement(Context context) {
File databaseFile = DatabaseHelper.getDatabasePath(context);
try {
final SQLiteDatabase writableDatabase = DatabaseHelper.getWritableDatabase(context);
//!!! throws SQLiteCantOpenDatabaseException("Failed to open database '/storage/emulated/0/databases/APhotoManager.db'") if no permission

PhotoChangeNotifyer.registerContentObserver(context, GlobalMediaContentObserver.getInstance(context));
final MediaDBRepository mediaDBRepository = new MediaDBRepository(writableDatabase);
final MediaContentproviderRepository mediaContentproviderRepository = new MediaContentproviderRepository(context);

} else {
PhotoChangeNotifyer.unregisterContentObserver(context, GlobalMediaContentObserver.getInstance(context));
if ((oldMediaDBApi != null) && (MediaContent2DBUpdateService.instance != null)) {
// switching from mediaImageDbReplacement to Contentprovider
MediaContent2DBUpdateService.instance.clearMediaCopy();
}
FotoSql.setMediaDBApi(mediaContentproviderRepository);
MediaContent2DBUpdateService.instance = null;
// read from copy database, write to both: copy-database and content-provider
final MergedMediaRepository mediaDBApi = new MergedMediaRepository(mediaDBRepository, mediaContentproviderRepository);
FotoSql.setMediaDBApi(mediaDBApi);

MediaContent2DBUpdateService.instance = new MediaContent2DBUpdateService(context, writableDatabase);

if (FotoSql.getCount(new QueryParameter().addWhere("1 = 1")) == 0) {
// database is empty; reload from Contentprovider
MediaContent2DBUpdateService.instance.rebuild(context, null);
}

PhotoChangeNotifyer.registerContentObserver(context, GlobalMediaContentObserver.getInstance(context));
return mediaDBApi;
} catch (RuntimeException ignore) {
Log.w(Global.LOG_CONTEXT,
"Cannot open Database (missing permissions) "
+ DatabaseHelper.getDatabasePath(context) + " "
+ ignore.getMessage(), ignore);
FotoSql.setMediaDBApi(new MediaDBRepositoryLoadOnDemand(context));
}
return null;
}

/**
* if Open Database failes because of missing File permissions
* postpone opening database until permission is granted
*/
private static class MediaDBRepositoryLoadOnDemand extends MediaRepositoryApiWrapper {

private final Context context;

public MediaDBRepositoryLoadOnDemand(Context context) {
super(null);
this.context = context;
}

@Override
protected IMediaRepositoryApi getReadChild() {
return registerAo10MediaImageDbReplacement(context);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,35 @@
package de.k3b.android.androFotoFinder.media;

import android.content.ContentValues;
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.StringUtils;
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 {
// 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;
private Boolean hasXmp;

public static void init() {
setFactory(new Factory() {
@Override
Expand All @@ -11,4 +38,90 @@ 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, false)) {
this.outPath = null; // failed
}

final IFile result = super.renameSouraceFileBeforeReplaceOrThrow(oldSourcefile, newName);
debugIdPaths("renameSouraceFileBeforeReplaceOrThrow end", oldSourcefile.getAbsolutePath(), newName);
return result;
}

@Override
protected void beforeCloseSaveOutputStream() {
if (this.outPath != null) {
renameInDatabase(":beforeCloseSaveOutputStream", this.outPath, this.inPath, true);
this.outPath = null;
}
super.beforeCloseSaveOutputStream();
}

// 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);

debugIdPaths(dbgContext + " renameInDatabase end " + execResultCount, fromPath, toPath);
if ((execResultCount != 1) && DBG_RENAME_IN_DB_ENABLED) {
// !!!! debug ausgabe path+ id failed
}
return 1 == execResultCount;
}

private void debugIdPaths(String dbgContext, String... paths) {
if (DBG_RENAME_IN_DB_ENABLED) {
StringBuilder sqlWhere = new StringBuilder();
for (String path : paths) {
if (sqlWhere.length() > 0) {
sqlWhere.append(" OR ");
}
sqlWhere.append("(").append(FotoSql.SQL_COL_PATH).append(" like '")
.append(FileUtils.replaceExtension(path, "")).append("%')");
}

// to prevent adding visibility
sqlWhere.append(" and " +
FotoSql.SQL_COL_EXT_MEDIA_TYPE +
" is not null");
final SelectedFiles selectedfiles = FotoSql.getSelectedfiles(sqlWhere.toString(), VISIBILITY.PRIVATE_PUBLIC);
Log.d(Global.LOG_CONTEXT, dbgContext + "\n\t["
+ StringUtils.appendMessage(null, paths)
+ "] :\n\t\t"
+ selectedfiles.toIdString() + " -> " + selectedfiles.toPathListString());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import java.io.File;

import de.k3b.android.androFotoFinder.transactionlog.TransactionLogSql;
import de.k3b.android.util.DatabaseContext;

Expand All @@ -39,20 +41,33 @@ public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION_2_MEDIA_DB_COPY = 2;

public static final int DATABASE_VERSION = DatabaseHelper.DATABASE_VERSION_2_MEDIA_DB_COPY;
public static final String DATABASE_NAME = "APhotoManager";

private static DatabaseHelper instance = null;
private static DatabaseContext databaseContext = null;

public DatabaseHelper(final Context context, final String databaseName) {
super(context, databaseName, null, DatabaseHelper.DATABASE_VERSION);
}

public static SQLiteDatabase getWritableDatabase(Context context) {
return getInstance(context).getWritableDatabase();
}

public static File getDatabasePath(Context context) {
getInstance(context);
return databaseContext.getDatabasePath(DATABASE_NAME);
}

private static DatabaseHelper getInstance(Context context) {
if (instance == null) {
instance = new DatabaseHelper(new DatabaseContext(context), "APhotoManager");
databaseContext = new DatabaseContext(context);
instance = new DatabaseHelper(databaseContext, DATABASE_NAME);
}
return instance.getWritableDatabase();
return instance;
}


public static void version2Upgrade_RecreateMediDbCopy(final SQLiteDatabase db) {
for (String sql : MediaDBRepository.Impl.DDL) {
db.execSQL(sql);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1127,9 +1127,9 @@ public static String getUriString(long imageID) {
return SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME + "/" + imageID;
}

public static SelectedFiles getSelectedfiles(String sqlWhere, VISIBILITY visibility) {
public static SelectedFiles getSelectedfiles(String sqlWhere, VISIBILITY visibility, String... parameters) {
QueryParameter query = new QueryParameter(FotoSql.queryChangePath);
query.addWhere(sqlWhere);
query.addWhere(sqlWhere, parameters);
query.addOrderBy(FotoSql.SQL_COL_PATH);

return getSelectedfiles(query, FotoSql.SQL_COL_PATH, visibility);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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();

Expand Down
Loading

0 comments on commit feaa790

Please sign in to comment.