diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/gallery/cursor/GalleryCursorFragment.java b/app/src/main/java/de/k3b/android/androFotoFinder/gallery/cursor/GalleryCursorFragment.java index 434ed4e5..41621815 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/gallery/cursor/GalleryCursorFragment.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/gallery/cursor/GalleryCursorFragment.java @@ -609,7 +609,7 @@ private void repairMissingDisplayNames() { @Override protected void doInBackground(Long id, Cursor cursor) { if (mPathColNo == -2) mPathColNo = cursor.getColumnIndex(FotoSql.SQL_COL_PATH); - mResultCount += PhotoPropertiesMediaFilesScanner.getInstance(getActivity()).updatePathRelatedFields(getActivity(), cursor, cursor.getString(mPathColNo), mColumnIndexPK, mPathColNo); + mResultCount += PhotoPropertiesMediaFilesScanner.getInstance(getActivity()).updatePathRelatedFields(cursor, cursor.getString(mPathColNo), mColumnIndexPK, mPathColNo); } @Override diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaDBRepository.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaDBRepository.java index e277c0c8..77353bba 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaDBRepository.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaDBRepository.java @@ -74,7 +74,7 @@ public class MediaDBRepository implements IMediaRepositoryApi { public static final String LOG_TAG = FotoSql.LOG_TAG + "DB"; // #155 - public static final boolean debugEnabledSqlRefresh = true; + public static final boolean DEBUG_ENABLED_SQL_REFRESH = true; private static final String MODUL_NAME = MediaDBRepository.class.getSimpleName(); private static String currentUpdateReason = null; @@ -87,13 +87,13 @@ public MediaDBRepository(SQLiteDatabase db) { } @Override - public Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgContext, + public Cursor createCursorForQuery(StringBuilder outDebugMessage, String dbgContext, QueryParameter parameters, VISIBILITY visibility, CancellationSignal cancellationSignal) { if (visibility != null) { FotoSql.setWhereVisibility(parameters, visibility); } - return createCursorForQuery(out_debugMessage, dbgContext, + return createCursorForQuery(outDebugMessage, dbgContext, parameters.toWhere(), parameters.toAndroidParameters(), parameters.toGroupBy(), parameters.toHaving(), parameters.toOrderBy(), @@ -102,11 +102,11 @@ public Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgCon } @Override - public Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgContext, String from, + public Cursor createCursorForQuery(StringBuilder outDebugMessage, String dbgContext, String from, String sqlWhereStatement, String[] sqlWhereParameters, String sqlSortOrder, CancellationSignal cancellationSignal, String... sqlSelectColums) { - return createCursorForQuery(out_debugMessage, dbgContext, + return createCursorForQuery(outDebugMessage, dbgContext, sqlWhereStatement, sqlWhereParameters, null, null, sqlSortOrder, cancellationSignal, sqlSelectColums); @@ -115,7 +115,7 @@ public Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgCon /** * every cursor query should go through this. adds logging if enabled */ - private Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgContext, + private Cursor createCursorForQuery(StringBuilder outDebugMessage, String dbgContext, String sqlWhereStatement, String[] selectionArgs, String groupBy, String having, String sqlSortOrder, CancellationSignal cancellationSignal, final String... sqlSelectColums) { @@ -125,24 +125,24 @@ private Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgCo try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - query = db.query(false, Impl.table, sqlSelectColums, sqlWhereStatement, selectionArgs, + query = db.query(false, Impl.DATABASE_TABLE_NAME, sqlSelectColums, sqlWhereStatement, selectionArgs, groupBy, having, sqlSortOrder, null, cancellationSignal); } else { - query = db.query(false, Impl.table, sqlSelectColums, sqlWhereStatement, selectionArgs, + query = db.query(false, Impl.DATABASE_TABLE_NAME, sqlSelectColums, sqlWhereStatement, selectionArgs, groupBy, having, sqlSortOrder, null); } } catch (Exception ex) { excpetion = ex; } finally { - if ((excpetion != null) || Global.debugEnabledSql || (out_debugMessage != null)) { + if ((excpetion != null) || Global.debugEnabledSql || (outDebugMessage != null)) { final int count = (query == null) ? 0 : query.getCount(); - StringBuilder message = StringUtils.appendMessage(out_debugMessage, excpetion, + StringBuilder message = StringUtils.appendMessage(outDebugMessage, excpetion, dbgContext, MODUL_NAME + ".createCursorForQuery:\n", - QueryParameter.toString(sqlSelectColums, null, Impl.table, sqlWhereStatement, + QueryParameter.toString(sqlSelectColums, null, Impl.DATABASE_TABLE_NAME, sqlWhereStatement, selectionArgs, sqlSortOrder, count)); - if (out_debugMessage == null) { + if (outDebugMessage == null) { Log.i(LOG_TAG, message.toString(), excpetion); } // else logging is done by caller } @@ -187,7 +187,7 @@ public int exexUpdateImpl(String dbgContext, ContentValues values, String sqlWhe int result = -1; Exception excpetion = null; try { - result = db.update(Impl.table, values, sqlWhere, selectionArgs); + result = db.update(Impl.DATABASE_TABLE_NAME, values, sqlWhere, selectionArgs); if (result != 0) { currentUpdateId++; currentUpdateReason = dbgContext; @@ -211,7 +211,7 @@ public ContentValues getDbContent(long id) { Cursor c = null; try { c = this.createCursorForQuery(null, "getDbContent", - Impl.table, FotoSql.FILTER_COL_PK, new String[]{"" + id}, null, null, "*"); + Impl.DATABASE_TABLE_NAME, FotoSql.FILTER_COL_PK, new String[]{"" + id}, null, null, "*"); if (c.moveToNext()) { ContentValues values = new ContentValues(); DatabaseUtils.cursorRowToContentValues(c, values); @@ -238,7 +238,7 @@ public Uri execInsert(String dbgContext, ContentValues values) { Exception excpetion = null; try { // on my android-4.4 insert with media_type=1001 (private) does insert with media_type=1 (image) - result = db.insertWithOnConflict(Impl.table, null, values, SQLiteDatabase.CONFLICT_REPLACE ); + result = db.insertWithOnConflict(Impl.DATABASE_TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); if (result == -1) { return null; } @@ -284,7 +284,7 @@ public int deleteMedia(String dbgContext, String where, String[] selectionArgs, lastUsedWhereClause = FotoSql.SQL_COL_PATH + " is null"; lastSelectionArgs = null; - delCount = db.delete(Impl.table, lastUsedWhereClause, lastSelectionArgs); + delCount = db.delete(Impl.DATABASE_TABLE_NAME, lastUsedWhereClause, lastSelectionArgs); if (Global.debugEnabledSql || LibGlobal.debugEnabledJpg) { Log.i(LOG_TAG, dbgContext + "-b: " + MODUL_NAME + @@ -293,7 +293,7 @@ public int deleteMedia(String dbgContext, String where, String[] selectionArgs, lastUsedWhereClause, lastSelectionArgs, null, delCount)); } } else { - delCount = db.delete(Impl.table, lastUsedWhereClause, lastSelectionArgs); + delCount = db.delete(Impl.DATABASE_TABLE_NAME, lastUsedWhereClause, lastSelectionArgs); if (Global.debugEnabledSql || LibGlobal.debugEnabledJpg) { Log.i(LOG_TAG, dbgContext + ": " + MODUL_NAME + @@ -330,7 +330,7 @@ public long getCurrentUpdateId() { @Override public boolean mustRequery(long updateId) { final boolean modified = currentUpdateId != updateId; - if (modified && MediaDBRepository.debugEnabledSqlRefresh) { + if (modified && MediaDBRepository.DEBUG_ENABLED_SQL_REFRESH) { Log.i(MediaDBRepository.LOG_TAG, "mustRequery: true because of " + currentUpdateReason); } return modified; @@ -397,11 +397,12 @@ public static ContentValues getContentValues(String fullFilePathFilter, ContentV public static class Impl { + public static final String DATABASE_TABLE_NAME = "files"; /** * SQL to create copy of contentprovider MediaStore.Images. * copied from android-4.4 android database. Removed columns not used */ - public static final String[] DDL = new String[]{ + protected static final String[] DDL = new String[]{ "DROP TABLE IF EXISTS \"files\"", "CREATE TABLE \"files\" (\n" + "\t_id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + @@ -431,8 +432,7 @@ public static class Impl { "CREATE INDEX sort_index ON files(datetaken ASC, _id ASC)", "CREATE INDEX title_idx ON files(title)", }; - - public static final String table = "files"; + private static final int COL_INT_MIN = 0; // same colum order as in DDL private static final String[] USED_MEDIA_COLUMNS = new String[]{ // INTEGER 0 .. 10 @@ -460,17 +460,22 @@ public static class Impl { SQL_COL_LAT, SQL_COL_LON, }; + private static final int COL_INT_MAX = 10; + private static final int COL_TXT_MIN = 11; + private static final int COL_TXT_MAX = 16; + private static final int COL_DBL_MIN = 17; + private static final int COL_DBL_MAX = 18; + // COL_ID must be col#0 to macke update where id=? work + private static final int COL_ID = COL_INT_MIN; // = 0 + private static final int COL_DATE_ADDED = 1; + private static final int COL_LAST_MODIFIED = 2; + // do not copy 6==SQL_COL_EXT_XMP_LAST_MODIFIED_DATE + // on android-10 and above wich is used as rescan marker + private static final int COL_INT_COPY_IGNORE = (Global.useAo10MediaImageDbReplacement) ? 6 : -1; + + private Impl() { + } - private static final int intMin = 0; - private static final int intMax = 10; - private static final int txtMin = 11; - private static final int txtMax = 16; - private static final int dblMin = 17; - private static final int dblMax = 18; - - private static final int colID = 0; - private static final int colDATE_ADDED = 1; - private static final int colLAST_MODIFIED = 2; private static final String FILTER_EXPR_AFFECTED_FILES = "(" + FotoSql.FILTER_EXPR_PRIVATE_PUBLIC + " OR " + SQL_COL_PATH + " like '%" + AlbumFile.SUFFIX_VALBUM + "' " @@ -484,23 +489,23 @@ public static class Impl { private static long nextMonthTimeInSecs; private static boolean isLomg(int index) { - return index >= intMin && index <= intMax; + return index >= COL_INT_MIN && index <= COL_INT_MAX; } // private Object get(Cursor cursor, columIndex) private static boolean isString(int index) { - return index >= txtMin && index <= txtMax; + return index >= COL_TXT_MIN && index <= COL_TXT_MAX; } private static boolean isDouble(int index) { - return index >= dblMin && index <= dblMax; + return index >= COL_DBL_MIN && index <= COL_DBL_MAX; } private static String getSqlInsertWithParams() { StringBuilder sql = new StringBuilder(); - sql.append("INSERT INTO ").append(table).append("(").append(USED_MEDIA_COLUMNS[0]); + sql.append("INSERT INTO ").append(DATABASE_TABLE_NAME).append("(").append(USED_MEDIA_COLUMNS[0]); for (int i = 1; i < USED_MEDIA_COLUMNS.length; i++) { sql.append(", ").append(USED_MEDIA_COLUMNS[i]); } @@ -515,7 +520,7 @@ private static String getSqlInsertWithParams() { private static String getSqlUpdateWithParams() { StringBuilder sql = new StringBuilder(); - sql.append("UPDATE ").append(table).append(" SET "); + sql.append("UPDATE ").append(DATABASE_TABLE_NAME).append(" SET "); for (int i = 1; i < USED_MEDIA_COLUMNS.length; i++) { if (i > 1) sql.append(", "); sql.append(USED_MEDIA_COLUMNS[i]).append("=?"); @@ -528,28 +533,28 @@ private static int bindAndExecUpdate(Cursor c, SQLiteStatement sql, long dateAdd sql.clearBindings(); // sql where - sql.bindLong(dblMax + 1, c.getLong(intMin)); + sql.bindLong(COL_DBL_MAX + 1, c.getLong(COL_ID)); - for (int i = intMin + 1; i <= intMax; i++) { - if (!c.isNull(i)) { + for (int i = COL_INT_MIN + 1; i <= COL_INT_MAX; i++) { + if (COL_INT_COPY_IGNORE != i && !c.isNull(i)) { sql.bindLong(i, c.getLong(i)); } } - for (int i = txtMin; i <= txtMax; i++) { + for (int i = COL_TXT_MIN; i <= COL_TXT_MAX; i++) { if (!c.isNull(i)) { sql.bindString(i, c.getString(i)); } } - for (int i = dblMin; i <= dblMax; i++) { + for (int i = COL_DBL_MIN; i <= COL_DBL_MAX; i++) { if (!c.isNull(i)) { sql.bindDouble(i, c.getDouble(i)); } } if (dateAdded != 0) { - sql.bindLong(colDATE_ADDED, dateAdded); + sql.bindLong(COL_DATE_ADDED, dateAdded); } if (dateUpdated != 0) { - sql.bindLong(colLAST_MODIFIED, dateUpdated); + sql.bindLong(COL_LAST_MODIFIED, dateUpdated); } return sql.executeUpdateDelete(); } @@ -557,26 +562,26 @@ private static int bindAndExecUpdate(Cursor c, SQLiteStatement sql, long dateAdd private static void bindAndExecInsert(Cursor c, SQLiteStatement sql, long dateAdded, long dateUpdated) { sql.clearBindings(); - for (int i = intMin; i <= intMax; i++) { - if (!c.isNull(i)) { + for (int i = COL_INT_MIN; i <= COL_INT_MAX; i++) { + if (COL_INT_COPY_IGNORE != i && !c.isNull(i)) { sql.bindLong(i + 1, c.getLong(i)); } } - for (int i = txtMin; i <= txtMax; i++) { + for (int i = COL_TXT_MIN; i <= COL_TXT_MAX; i++) { if (!c.isNull(i)) { sql.bindString(i + 1, c.getString(i)); } } - for (int i = dblMin; i <= dblMax; i++) { + for (int i = COL_DBL_MIN; i <= COL_DBL_MAX; i++) { if (!c.isNull(i)) { sql.bindDouble(i + 1, c.getDouble(i)); } } if (dateAdded != 0) { - sql.bindLong(colDATE_ADDED + 1, dateAdded); + sql.bindLong(COL_DATE_ADDED + 1, dateAdded); } if (dateUpdated != 0) { - sql.bindLong(colLAST_MODIFIED + 1, dateUpdated); + sql.bindLong(COL_LAST_MODIFIED + 1, dateUpdated); } sql.executeInsert(); } @@ -602,10 +607,9 @@ private static ContentValues getContentValues(Cursor cursor, ContentValues desti public static void clearMedaiCopy(SQLiteDatabase db) { try { - db.execSQL("DROP TABLE " + table); + db.execSQL("DROP TABLE " + DATABASE_TABLE_NAME); } catch (Exception ex) { // Log.e(LOG_TAG, "FotoSql.execGetFotoPaths() Cannot get path from: " + FotoSql.SQL_COL_PATH + " like '" + pathFilter +"'", ex); - } finally { } } @@ -665,13 +669,13 @@ public static int updateMediaCopy( long maxDateUpdatedSecs = 0; while (c.moveToNext()) { - long curDateAddedSecs = getDateInSecs(c, colDATE_ADDED); + long curDateAddedSecs = getDateInSecs(c, COL_DATE_ADDED); if (curDateAddedSecs > maxDateAddedSecs) { maxDateAddedSecs = curDateAddedSecs; } isUpdate = (curDateAddedSecs <= filterLastUpdateMinInMillis / FotoSql.LAST_MODIFIED_FACTOR); - long curDateUpdatedSecs = getDateInSecs(c, colLAST_MODIFIED); + long curDateUpdatedSecs = getDateInSecs(c, COL_LAST_MODIFIED); if (curDateUpdatedSecs > maxDateUpdatedSecs) { maxDateUpdatedSecs = curDateUpdatedSecs; } @@ -698,11 +702,10 @@ public static int updateMediaCopy( lastSql = null; - if ((progessListener != null) && (progress % 100) == 0) { - if (!progessListener.onProgress(progress, itemCount, context.getString(R.string.scanner_update_result_format, progress))) { - // canceled in gui thread - return -progress; - } + if ((progessListener != null) && (progress % 100) == 0 && + !progessListener.onProgress(progress, itemCount, context.getString(R.string.scanner_update_result_format, progress))) { + // canceled in gui thread + return -progress; } progress++; } // while over all old items @@ -760,17 +763,5 @@ protected static long getDateInSecs(Cursor c, int colPosition) { } return dateInSecs; } - - private static void save(SQLiteDatabase db, Cursor c, ContentValues contentValues, long lastUpdate) { - boolean isNew = (c.getLong(colDATE_ADDED) > lastUpdate); - - if (isNew) { - db.insert(table, null, contentValues); - } else { - String[] params = new String[]{"" + c.getLong(colID)}; - contentValues.remove(SQL_COL_PK); - db.update(table, contentValues, FotoSql.FILTER_COL_PK, params); - } - } } } diff --git a/app/src/main/java/de/k3b/android/util/PhotoPropertiesMediaFilesScanner.java b/app/src/main/java/de/k3b/android/util/PhotoPropertiesMediaFilesScanner.java index 84050e54..e89fd845 100644 --- a/app/src/main/java/de/k3b/android/util/PhotoPropertiesMediaFilesScanner.java +++ b/app/src/main/java/de/k3b/android/util/PhotoPropertiesMediaFilesScanner.java @@ -29,7 +29,6 @@ import android.media.MediaScannerConnection; import android.os.Build; import android.provider.MediaStore; -import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; @@ -45,9 +44,7 @@ import de.k3b.LibGlobal; import de.k3b.android.androFotoFinder.Global; import de.k3b.android.androFotoFinder.media.PhotoPropertiesMediaDBContentValues; -import de.k3b.android.androFotoFinder.queries.FotoSql; import de.k3b.android.androFotoFinder.queries.IMediaRepositoryApi; -import de.k3b.android.androFotoFinder.tagDB.TagSql; import de.k3b.database.QueryParameter; import de.k3b.geo.api.GeoPointDto; import de.k3b.geo.api.IGeoPointInfo; @@ -60,13 +57,36 @@ import de.k3b.media.PhotoPropertiesXmpSegment; import de.k3b.tagDB.TagRepository; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.EXT_LAST_EXT_SCAN_NO_XMP; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_EXT_DESCRIPTION; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_EXT_RATING; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_EXT_TAGS; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_EXT_TITLE; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_LAST_MODIFIED; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_LAT; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_LON; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_PATH; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_PK; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_SIZE; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.addDateAdded; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.execDeleteByPath; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.execGetPathIdMap; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.fixPrivate; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.getMediaDBApi; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.getWhereInFileNames; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.queryChangePath; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.setVisibility; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.setWhereFileNames; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.setXmpFileModifyDate; + /** * Android Media Scanner for images/photos/jpg compatible with android-5.0 Media scanner. * This Class handles standard Android-5.0 image fields. - * + *

* Since android.media.MediaScannerConnection does not work on my android-4.2 * here is my own implementation. - * + *

* Created by k3b on 14.09.2015. */ public abstract class PhotoPropertiesMediaFilesScanner { @@ -78,15 +98,15 @@ public abstract class PhotoPropertiesMediaFilesScanner { protected static final String DB_LATITUDE = MediaStore.Images.Media.LATITUDE; */ - protected static final String DB_TITLE = FotoSql.SQL_COL_EXT_TITLE; + protected static final String DB_TITLE = SQL_COL_EXT_TITLE; private static final String DB_WIDTH = MediaStore.MediaColumns.WIDTH; private static final String DB_HEIGHT = MediaStore.MediaColumns.HEIGHT; private static final String DB_MIME_TYPE = MediaStore.MediaColumns.MIME_TYPE; protected static final String DB_ORIENTATION = MediaStore.Images.Media.ORIENTATION; - protected static final String DB_DATA = FotoSql.SQL_COL_PATH; // _data + protected static final String DB_DATA = SQL_COL_PATH; // _data protected static final String DB_DISPLAY_NAME = MediaStore.MediaColumns.DISPLAY_NAME; - private static final String DB_SIZE = FotoSql.SQL_COL_SIZE; + private static final String DB_SIZE = SQL_COL_SIZE; public static final int DEFAULT_SCAN_DEPTH = 22; public static final String MEDIA_IGNORE_FILENAME = FileUtils.MEDIA_IGNORE_FILENAME; @@ -98,7 +118,7 @@ public abstract class PhotoPropertiesMediaFilesScanner { public final Context mContext; - private Map noMediaCache = new HashMap<>(); + private final Map noMediaCache = new HashMap<>(); public PhotoPropertiesMediaFilesScanner(Context context) { mContext = context.getApplicationContext(); @@ -108,9 +128,9 @@ public PhotoPropertiesMediaFilesScanner(Context context) { public static void notifyChanges(Context context, String why) { if (Global.debugEnabled) { Log.i(Global.LOG_CONTEXT, CONTEXT + "notifyChanges(" + why + ") " - + FotoSql.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE); + + SQL_TABLE_EXTERNAL_CONTENT_URI_FILE); } - context.getContentResolver().notifyChange(FotoSql.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, null); + context.getContentResolver().notifyChange(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, null); } public static boolean isNoMedia(int maxLevel, IFile[] pathNames) { @@ -125,10 +145,6 @@ public static boolean isNoMedia(int maxLevel, IFile[] pathNames) { return false; } - public static boolean isNoMedia(IFile path) { - return isNoMedia(path, PhotoPropertiesMediaFilesScanner.DEFAULT_SCAN_DEPTH); - } - public static boolean isNoMedia(IFile path, int maxLevel) { return isNoMedia(path, maxLevel, null); } @@ -172,7 +188,7 @@ public static int hideFolderMedia(Activity context, String path) { if (Global.debugEnabled) { Log.i(Global.LOG_CONTEXT, CONTEXT + " hideFolderMedia: delete from media db " + path + "/**"); } - result = FotoSql.execDeleteByPath(CONTEXT + " hideFolderMedia", path, VISIBILITY.PRIVATE_PUBLIC); + result = execDeleteByPath(CONTEXT + " hideFolderMedia", path, VISIBILITY.PRIVATE_PUBLIC); if (result > 0) { PhotoPropertiesMediaFilesScanner.notifyChanges(context, "hide " + path + "/**"); } @@ -181,8 +197,33 @@ public static int hideFolderMedia(Activity context, String path) { return result; } + /** + * in secs since 1970 + */ + protected static long getXmpFilelastModified(PhotoPropertiesXmpSegment xmpContent) { + long xmpFilelastModified = 0; + if (xmpContent != null) { + xmpFilelastModified = xmpContent.getFilelastModified(); + } + if (xmpFilelastModified == 0) { + xmpFilelastModified = EXT_LAST_EXT_SCAN_NO_XMP; + } + return xmpFilelastModified; + } + + /** + * sets the path related fields + */ + public static String setFileFields(ContentValues values, File file) { + String newAbsolutePath = FileUtils.tryGetCanonicalPath(file, file.getAbsolutePath()); + setPathRelatedFieldsIfNeccessary(values, newAbsolutePath, null); + values.put(SQL_COL_LAST_MODIFIED, file.lastModified() / 1000); + values.put(DB_SIZE, file.length()); + return newAbsolutePath; + } + public int updateMediaDatabaseAndroid42(Context context, IFile[] oldPathNames, IFile... newPathNames) { - IMediaRepositoryApi api = FotoSql.getMediaDBApi(); + IMediaRepositoryApi api = getMediaDBApi(); try { api.beginTransaction(); final boolean hasNew = excludeNomediaFiles(newPathNames) > 0; @@ -195,9 +236,9 @@ public int updateMediaDatabaseAndroid42(Context context, IFile[] oldPathNames, I result = deleteInMediaDatabase(oldPathNames); } if (hasNew) { - result = insertIntoMediaDatabase(context, newPathNames); + result = insertIntoMediaDatabase(newPathNames); } - TagSql.fixPrivate(); + fixPrivate(); api.setTransactionSuccessful(); return result; } finally { @@ -206,7 +247,7 @@ public int updateMediaDatabaseAndroid42(Context context, IFile[] oldPathNames, I } /** - * Replace all files that either non-jpg or in ".nomedia" folder with null so they wont be + * Replace all files that either non-jpg or in ".nomedia" folder with null so they will not be * processed by media scanner * * @return number of items left. @@ -230,7 +271,7 @@ private int excludeNomediaFiles(IFile[] fullPathNames) { return itemsLeft; } - private int insertIntoMediaDatabase(Context context, IFile[] newPathNames) { + private int insertIntoMediaDatabase(IFile[] newPathNames) { int modifyCount = 0; if ((newPathNames != null) && (newPathNames.length > 0)) { @@ -238,7 +279,7 @@ private int insertIntoMediaDatabase(Context context, IFile[] newPathNames) { Log.i(Global.LOG_CONTEXT, CONTEXT + "A42 scanner starting with " + newPathNames.length + " files " + newPathNames[0] + "..."); } - Map inMediaDb = FotoSql.execGetPathIdMap(newPathNames); + Map inMediaDb = execGetPathIdMap(newPathNames); for (IFile fileName : newPathNames) { if (fileName != null) { @@ -265,9 +306,9 @@ private int deleteInMediaDatabase(IFile[] oldPathNames) { int modifyCount = 0; if ((oldPathNames != null) && (oldPathNames.length > 0)) { - String sqlWhere = FotoSql.getWhereInFileNames(oldPathNames); + String sqlWhere = getWhereInFileNames(oldPathNames); try { - modifyCount = FotoSql.getMediaDBApi().deleteMedia(CONTEXT + "deleteInMediaDatabase", sqlWhere, null, true); + modifyCount = getMediaDBApi().deleteMedia(CONTEXT + "deleteInMediaDatabase", sqlWhere, null, true); if (Global.debugEnabled) { Log.d(Global.LOG_CONTEXT, CONTEXT + "deleteInMediaDatabase(len=" + oldPathNames.length + ", files='" + oldPathNames[0] + "'...) result count=" + modifyCount); } @@ -279,6 +320,13 @@ private int deleteInMediaDatabase(IFile[] oldPathNames) { return modifyCount; } + /** + * updates values with current values of file + */ + public PhotoPropertiesMediaDBContentValues getExifFromFile(IFile jpgFile) { + return getExifFromFile(createDefaultContentValues(), jpgFile); + } + /** * change path and path dependant fields in media database */ @@ -310,26 +358,31 @@ private int renameInMediaDatabase(Context context, IFile[] oldPathNames, IFile.. int modifyCount = deleteInMediaDatabase(deleteFileNames.toArray(new IFile[deleteFileNames.size()])) + renameInMediaDatabase(context, old2NewFileNames) - + insertIntoMediaDatabase(context, insertFileNames.toArray(new IFile[insertFileNames.size()])); + + insertIntoMediaDatabase(insertFileNames.toArray(new IFile[insertFileNames.size()])); return modifyCount; } return 0; } + private void updateTagRepository(List tags) { + TagRepository tagRepository = TagRepository.getInstance(); + tagRepository.includeTagNamesIfNotFound(tags); + } + private int renameInMediaDatabase(Context context, Map old2NewFileNames) { int modifyCount = 0; if (old2NewFileNames.size() > 0) { - QueryParameter query = new QueryParameter(FotoSql.queryChangePath); - FotoSql.setWhereFileNames(query, old2NewFileNames.keySet().toArray(new String[old2NewFileNames.size()])); + QueryParameter query = new QueryParameter(queryChangePath); + setWhereFileNames(query, old2NewFileNames.keySet().toArray(new String[old2NewFileNames.size()])); Cursor c = null; try { - c = FotoSql.getMediaDBApi().createCursorForQuery(null, "renameInMediaDatabase", query, VISIBILITY.PRIVATE_PUBLIC, null); - int pkColNo = c.getColumnIndex(FotoSql.SQL_COL_PK); - int pathColNo = c.getColumnIndex(FotoSql.SQL_COL_PATH); + c = getMediaDBApi().createCursorForQuery(null, "renameInMediaDatabase", query, VISIBILITY.PRIVATE_PUBLIC, null); + int pkColNo = c.getColumnIndex(SQL_COL_PK); + int pathColNo = c.getColumnIndex(SQL_COL_PATH); while (c.moveToNext()) { String oldPath = c.getString(pathColNo); - modifyCount += updatePathRelatedFields(context, c, old2NewFileNames.get(oldPath), pkColNo, pathColNo); + modifyCount += updatePathRelatedFields(c, old2NewFileNames.get(oldPath), pkColNo, pathColNo); } } catch (Exception ex) { Log.e(Global.LOG_CONTEXT, CONTEXT + "execChangePaths() error :", ex); @@ -344,12 +397,39 @@ private int renameInMediaDatabase(Context context, Map old2NewFi return modifyCount; } - /** updates values with current values of file */ - public PhotoPropertiesMediaDBContentValues getExifFromFile(IFile jpgFile) { - return getExifFromFile(createDefaultContentValues(), jpgFile); + + protected IGeoPointInfo getPositionFromMeta(String absoluteJpgPath, String id, IPhotoProperties exif) { + if (exif != null) { + Double latitude = exif.getLatitude(); + if (latitude != null) { + return new GeoPointDto(latitude, exif.getLongitude(), GeoPointDto.NO_ZOOM).setId(id); + } + PhotoPropertiesXmpSegment xmpContent = PhotoPropertiesXmpSegment.loadXmpSidecarContentOrNull(absoluteJpgPath, "getPositionFromFile"); + if (xmpContent != null) { + latitude = xmpContent.getLatitude(); + if (latitude != null) { + return new GeoPointDto(latitude, xmpContent.getLongitude(), GeoPointDto.NO_ZOOM).setId(id); + } + } + } + + return null; + } + + protected abstract IPhotoProperties loadNonMediaValues(ContentValues destinationValues, IFile jpgFile, IPhotoProperties xmpContent); + + /** + * @return number of copied properties + */ + protected int getExifValues(PhotoPropertiesMediaDBContentValues dest, IPhotoProperties src) { + return PhotoPropertiesUtil.copyNonEmpty(dest, src); } - /** updates values with current values of file. */ + public abstract IGeoPointInfo getPositionFromFile(String absolutePath, String id); + + /** + * updates values with current values of file. + */ protected PhotoPropertiesMediaDBContentValues getExifFromFile(ContentValues values, IFile jpgFile) { try { BitmapFactory.Options options = new BitmapFactory.Options(); @@ -362,7 +442,7 @@ protected PhotoPropertiesMediaDBContentValues getExifFromFile(ContentValues valu PhotoPropertiesXmpSegment xmpContent = PhotoPropertiesXmpSegment.loadXmpSidecarContentOrNull(jpgFile, "getExifFromFile"); final long xmpFilelastModified = getXmpFilelastModified(xmpContent); - values.put(FotoSql.SQL_COL_LAST_MODIFIED, jpgFile.lastModified() / 1000); + values.put(SQL_COL_LAST_MODIFIED, jpgFile.lastModified() / 1000); values.put(DB_SIZE, jpgFile.length()); if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) && mWidth > 0 && mHeight > 0) { @@ -371,7 +451,7 @@ protected PhotoPropertiesMediaDBContentValues getExifFromFile(ContentValues valu } values.put(DB_MIME_TYPE, imageType); - TagSql.setXmpFileModifyDate(values, xmpFilelastModified); + setXmpFileModifyDate(values, xmpFilelastModified); IPhotoProperties exif = loadNonMediaValues(values, jpgFile, xmpContent); @@ -396,7 +476,7 @@ protected PhotoPropertiesMediaDBContentValues getExifFromFile(ContentValues valu if (visibility == null) { visibility = VISIBILITY.getVisibility(src); } - FotoSql.setVisibility(values, visibility); + setVisibility(values, visibility); } String absoluteJpgPath = jpgFile.getCanonicalPath(); @@ -409,79 +489,28 @@ protected PhotoPropertiesMediaDBContentValues getExifFromFile(ContentValues valu return null; } - private void updateTagRepository(List tags) { - TagRepository tagRepository = TagRepository.getInstance(); - tagRepository.includeTagNamesIfNotFound(tags); - } - - /** in secs since 1970 */ - protected static long getXmpFilelastModified(PhotoPropertiesXmpSegment xmpContent) { - long xmpFilelastModified = 0; - if (xmpContent != null) { - xmpFilelastModified = xmpContent.getFilelastModified(); - } - if (xmpFilelastModified == 0) { - xmpFilelastModified = TagSql.EXT_LAST_EXT_SCAN_NO_XMP; - } - return xmpFilelastModified; - } - - - protected IGeoPointInfo getPositionFromMeta(String absoluteJpgPath, String id, IPhotoProperties exif) { - if (exif != null) { - Double latitude = exif.getLatitude(); - if (latitude != null) { - return new GeoPointDto(latitude, exif.getLongitude(), GeoPointDto.NO_ZOOM).setId(id); - } - PhotoPropertiesXmpSegment xmpContent = PhotoPropertiesXmpSegment.loadXmpSidecarContentOrNull(absoluteJpgPath, "getPositionFromFile"); - if (xmpContent != null) { - latitude = xmpContent.getLatitude(); - if (latitude != null) { - return new GeoPointDto(latitude, xmpContent.getLongitude(), GeoPointDto.NO_ZOOM).setId(id); - } - } - } - - return null; - } - - protected abstract IPhotoProperties loadNonMediaValues(ContentValues destinationValues, IFile jpgFile, IPhotoProperties xmpContent); - - /** @return number of copied properties */ - protected int getExifValues(PhotoPropertiesMediaDBContentValues dest, IPhotoProperties src) { - return PhotoPropertiesUtil.copyNonEmpty(dest, src); - } - - public abstract IGeoPointInfo getPositionFromFile(String absolutePath, String id); public int updatePathRelatedFields(Context context, Cursor cursor, String newAbsolutePath) { - int columnIndexPk = cursor.getColumnIndex(FotoSql.SQL_COL_PK); - int columnIndexPath = cursor.getColumnIndex(FotoSql.SQL_COL_PATH); - return updatePathRelatedFields(context, cursor, newAbsolutePath, columnIndexPk, columnIndexPath); - } - - public int updatePathRelatedFields(Context context, Cursor cursor, String newAbsolutePath, int columnIndexPk, int columnIndexPath) { - ContentValues values = new ContentValues(); - DatabaseUtils.cursorRowToContentValues(cursor, values); - String oldAbsolutePath = cursor.getString(columnIndexPath); - int id = cursor.getInt(columnIndexPk); - setPathRelatedFieldsIfNeccessary(values, newAbsolutePath , oldAbsolutePath); - return FotoSql.getMediaDBApi().execUpdate("updatePathRelatedFields", id, values); + int columnIndexPk = cursor.getColumnIndex(SQL_COL_PK); + int columnIndexPath = cursor.getColumnIndex(SQL_COL_PATH); + return updatePathRelatedFields(cursor, newAbsolutePath, columnIndexPk, columnIndexPath); } - /** sets the path related fields */ + /** + * sets the path related fields + */ private static void setPathRelatedFieldsIfNeccessary(ContentValues values, String newAbsolutePath, String oldAbsolutePath) { setFieldIfNeccessary(values, DB_TITLE, generateTitleFromFilePath(newAbsolutePath), generateTitleFromFilePath(oldAbsolutePath)); setFieldIfNeccessary(values, DB_DISPLAY_NAME, generateDisplayNameFromFilePath(newAbsolutePath), generateDisplayNameFromFilePath(oldAbsolutePath)); values.put(DB_DATA, newAbsolutePath); } - /** sets the path related fields */ - public static String setFileFields(ContentValues values, File file) { - String newAbsolutePath = FileUtils.tryGetCanonicalPath(file, file.getAbsolutePath()); - setPathRelatedFieldsIfNeccessary(values, newAbsolutePath, null); - values.put(FotoSql.SQL_COL_LAST_MODIFIED, file.lastModified() / 1000); - values.put(DB_SIZE, file.length()); - return newAbsolutePath; + public int updatePathRelatedFields(Cursor cursor, String newAbsolutePath, int columnIndexPk, int columnIndexPath) { + ContentValues values = new ContentValues(); + DatabaseUtils.cursorRowToContentValues(cursor, values); + String oldAbsolutePath = cursor.getString(columnIndexPath); + int id = cursor.getInt(columnIndexPk); + setPathRelatedFieldsIfNeccessary(values, newAbsolutePath, oldAbsolutePath); + return getMediaDBApi().execUpdate("updatePathRelatedFields", id, values); } @@ -497,9 +526,9 @@ private int updateAndroid42(String dbgContext, long id, IFile file) { if ((file != null) && file.exists() && file.canRead()) { ContentValues values = createDefaultContentValues(); getExifFromFile(values, file); - return FotoSql.getMediaDBApi().execUpdate(dbgContext, id, values); + return getMediaDBApi().execUpdate(dbgContext, id, values); } - return 0; + return 0; } protected ContentValues createDefaultContentValues() { @@ -507,21 +536,21 @@ protected ContentValues createDefaultContentValues() { // to allow set null because copy does not setNull if already has null (not found) contentValues.putNull(DB_TITLE); - contentValues.putNull(FotoSql.SQL_COL_LON); - contentValues.putNull(FotoSql.SQL_COL_LAT); - contentValues.putNull(TagSql.SQL_COL_EXT_DESCRIPTION); - contentValues.putNull(TagSql.SQL_COL_EXT_TAGS); - contentValues.putNull(TagSql.SQL_COL_EXT_RATING); + contentValues.putNull(SQL_COL_LON); + contentValues.putNull(SQL_COL_LAT); + contentValues.putNull(SQL_COL_EXT_DESCRIPTION); + contentValues.putNull(SQL_COL_EXT_TAGS); + contentValues.putNull(SQL_COL_EXT_RATING); return contentValues; } private int insertAndroid42(String dbgContext, IFile file) { if ((file != null) && file.exists() && file.canRead()) { ContentValues values = createDefaultContentValues(); - FotoSql.addDateAdded(values); + addDateAdded(values); IPhotoProperties exif = getExifFromFile(values, file); - return (null != FotoSql.getMediaDBApi().insertOrUpdateMediaDatabase(dbgContext, exif.getPath(), values, exif.getVisibility(), 1L)) ? 1 : 0; + return (null != getMediaDBApi().insertOrUpdateMediaDatabase(dbgContext, exif.getPath(), values, exif.getVisibility(), 1L)) ? 1 : 0; } return 0; } diff --git a/app/src/main/java/de/k3b/android/util/RecursivePhotoPropertiesMediaFilesScannerAsyncTask.java b/app/src/main/java/de/k3b/android/util/RecursivePhotoPropertiesMediaFilesScannerAsyncTask.java index 37c7c6cb..e47b9a00 100644 --- a/app/src/main/java/de/k3b/android/util/RecursivePhotoPropertiesMediaFilesScannerAsyncTask.java +++ b/app/src/main/java/de/k3b/android/util/RecursivePhotoPropertiesMediaFilesScannerAsyncTask.java @@ -88,90 +88,96 @@ public RecursivePhotoPropertiesMediaFilesScannerAsyncTask( protected Integer doInBackground(IFile[]... pathNames) { // do not call super.doInBackground here because logic is different int resultCount = 0; + FileDirectoryMediaCollector fileDirectoryMediaCollector = new FileDirectoryMediaCollector(); for (IFile[] pathArray : pathNames) { if (pathArray != null) { for (IFile pathName : pathArray) { - resultCount += scanRoot(pathName); + resultCount += fileDirectoryMediaCollector.scanRootDir(pathName); } } } return resultCount; } - private int scanRoot(IFile rootPath) { - int resultCount = 0; - if ((rootPath != null) && (rootPath.length() > 0)) { - if (this.rescanNeverScannedByAPM) { - List pathsToUpdate = TagSql.getPhotosNeverScanned(rootPath.getAbsolutePath()); - for (String pathToUpdate : pathsToUpdate) { - resultCount += scanDirOrFile(FileFacade.convert("scanRoot incremental paths never scanned ", pathToUpdate)); + /** + * find media files by traversing File-(Sub-)Directories + */ + private class FileDirectoryMediaCollector { + private int scanRootDir(IFile rootPath) { + int resultCount = 0; + if ((rootPath != null) && (rootPath.length() > 0)) { + if (rescanNeverScannedByAPM) { + List pathsToUpdate = TagSql.getPhotosNeverScanned(rootPath.getAbsolutePath()); + for (String pathToUpdate : pathsToUpdate) { + resultCount += scanDirOrFile(FileFacade.convert("scanRoot incremental paths never scanned ", pathToUpdate)); + } } - } - resultCount += scanDirOrFile(rootPath); + resultCount += scanDirOrFile(rootPath); + } + return resultCount; } - return resultCount; - } - private int scanDirOrFile(IFile file) { - int resultCount = 0; - final String fullFilePath = file.getAbsolutePath(); - if (fullFilePath != null) { - if (!isCancelled()) { - if (file.isDirectory()) { - resultCount += scanDir(file, fullFilePath); - } else if (PhotoPropertiesUtil.isImage(file.getName(), PhotoPropertiesUtil.IMG_TYPE_ALL)) { - resultCount += runScanner(fullFilePath, file); + private int scanDirOrFile(IFile file) { + int resultCount = 0; + final String fullFilePath = file.getAbsolutePath(); + if (fullFilePath != null) { + if (!isCancelled()) { + if (file.isDirectory()) { + resultCount += scanDir(file, fullFilePath); + } else if (PhotoPropertiesUtil.isImage(file.getName(), PhotoPropertiesUtil.IMG_TYPE_ALL)) { + resultCount += runScanner(fullFilePath, file); + } + } else if (mPaused != null) { + mPaused.add(file); } - } else if (mPaused != null) { - mPaused.add(file); } + return resultCount; } - return resultCount; - } - private int scanDir(IFile file, String fullFilePath) { - int resultCount = 0; - if (this.scanForDeleted) { - List deletedPaths = null; - List existing = FotoSql.getPathsOfFolderWithoutSubfolders(file.getAbsolutePath()); - for (String candidatePath : existing) { - IFile camdidateFile = FileFacade.convert( - "RecursivePhotoPropertiesMediaFilesScannerAsyncTask.scanDir find deleted", candidatePath); - // delete in db for existing but not found as file - if (!camdidateFile.exists()) { - if (deletedPaths == null) deletedPaths = new ArrayList<>(); - deletedPaths.add(candidatePath); + private int scanDir(IFile file, String fullFilePath) { + int resultCount = 0; + if (scanForDeleted) { + List deletedPaths = null; + List existing = FotoSql.getPathsOfFolderWithoutSubfolders(file.getAbsolutePath()); + for (String candidatePath : existing) { + IFile camdidateFile = FileFacade.convert( + "RecursivePhotoPropertiesMediaFilesScannerAsyncTask.scanDir find deleted", candidatePath); + // delete in db for existing but not found as file + if (!camdidateFile.exists()) { + if (deletedPaths == null) deletedPaths = new ArrayList<>(); + deletedPaths.add(candidatePath); + } + } + if (deletedPaths != null) { + FotoSql.deleteMedia("del photos that did not exist any more ", + deletedPaths, true); + resultCount += deletedPaths.size(); } } - if (deletedPaths != null) { - FotoSql.deleteMedia("del photos that did not exist any more ", - deletedPaths, true); - resultCount += deletedPaths.size(); - } - } - if (this.fullScan) { - IFile[] childFileNames = file.listFiles(); + if (fullScan) { + IFile[] childFileNames = file.listFiles(); - if (childFileNames != null) { - resultCount += runScanner(fullFilePath, childFileNames); + if (childFileNames != null) { + resultCount += runScanner(fullFilePath, childFileNames); + } } - } - if (this.fullScan || this.scanForDeleted) { - IFile[] subDirs = file.listFiles(); + if (fullScan || scanForDeleted) { + IFile[] subDirs = file.listFiles(); - if (subDirs != null) { - // #33 - for (IFile subDir : subDirs) { - if ((subDir != null) && (subDir.isDirectory()) && (!subDir.getName().startsWith("."))) { - resultCount += scanDirOrFile(subDir); + if (subDirs != null) { + // #33 + for (IFile subDir : subDirs) { + if ((subDir != null) && (subDir.isDirectory()) && (!subDir.getName().startsWith("."))) { + resultCount += scanDirOrFile(subDir); + } } } } + return resultCount; } - return resultCount; } /** @return true if scanner was resumable and started resume operation. */