diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/backup/Backup2ZipService.java b/app/src/main/java/de/k3b/android/androFotoFinder/backup/Backup2ZipService.java index 2c9e1a1e..7de70da1 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/backup/Backup2ZipService.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/backup/Backup2ZipService.java @@ -19,10 +19,8 @@ package de.k3b.android.androFotoFinder.backup; -import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; -import android.net.Uri; import android.support.annotation.NonNull; import android.util.Log; @@ -33,6 +31,7 @@ import de.k3b.android.androFotoFinder.Global; import de.k3b.android.androFotoFinder.R; import de.k3b.android.androFotoFinder.media.PhotoPropertiesMediaDBCursor; +import de.k3b.android.androFotoFinder.queries.ContentProviderMediaExecuter; import de.k3b.android.androFotoFinder.queries.FotoSql; import de.k3b.android.androFotoFinder.tagDB.TagSql; import de.k3b.database.QueryParameter; @@ -208,13 +207,12 @@ public static QueryParameter getEffectiveQueryParameter(@NonNull IZipConfig zipC /** calls consumers for each found query-result-item */ private void execQuery(QueryParameter query, IItemSaver... consumers) { - ContentResolver contentResolver = context.getContentResolver(); - Cursor cursor = null; try { this.onProgress(0,0, "Calculate"); - cursor = contentResolver.query(Uri.parse(query.toFrom()), query.toColumns(), - query.toAndroidWhere(), query.toAndroidParameters(), query.toOrderBy()); + cursor = ContentProviderMediaExecuter.createCursorForQuery( + null, "ZipExecute", context, + query, null); int itemCount = cursor.getCount(); diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryLoaderTask.java b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryLoaderTask.java index b5ddfbd7..67df3563 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryLoaderTask.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryLoaderTask.java @@ -22,13 +22,13 @@ import android.app.Activity; import android.content.Context; import android.database.Cursor; -import android.net.Uri; import android.os.AsyncTask; import android.util.Log; import java.util.List; import de.k3b.android.androFotoFinder.Global; +import de.k3b.android.androFotoFinder.queries.ContentProviderMediaExecuter; import de.k3b.android.androFotoFinder.queries.FotoSql; import de.k3b.database.QueryParameter; import de.k3b.io.Directory; @@ -102,8 +102,9 @@ protected IDirectory doInBackground(QueryParameter... queryParameter) { } try { - cursor = context.getContentResolver().query(Uri.parse(queryParameters.toFrom()), queryParameters.toColumns(), - queryParameters.toAndroidWhere(), queryParameters.toAndroidParameters(), queryParameters.toOrderBy()); + cursor = ContentProviderMediaExecuter.createCursorForQuery( + null, "ZipExecute", context, + queryParameters, null); int itemCount = cursor.getCount(); final int expectedCount = itemCount + itemCount; diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryPickerFragment.java b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryPickerFragment.java index 9265901c..1806edc3 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryPickerFragment.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/directory/DirectoryPickerFragment.java @@ -62,6 +62,7 @@ import de.k3b.android.androFotoFinder.ThumbNailUtils; import de.k3b.android.androFotoFinder.backup.BackupActivity; import de.k3b.android.androFotoFinder.imagedetail.ImageDetailMetaDialogBuilder; +import de.k3b.android.androFotoFinder.queries.ContentProviderMediaExecuter; import de.k3b.android.androFotoFinder.queries.FotoSql; import de.k3b.android.androFotoFinder.queries.FotoThumbSql; import de.k3b.android.androFotoFinder.queries.FotoViewerParameter; @@ -692,9 +693,9 @@ private boolean fixLinks(IDirectory linkDir) { if (cann == null) { // rename linkFile to canonicalFile updateValues.put(FotoSql.SQL_COL_PATH, canonicalPath + lin.substring(linkPath.length())); - FotoSql.execUpdate("fixLinks", context, linkIds[i].intValue() ,updateValues); + ContentProviderMediaExecuter.execUpdate("fixLinks", context, linkIds[i].intValue(), updateValues); } else { - FotoSql.deleteMedia("DirectoryPickerFragment.fixLinks", context, FotoSql.FILTER_COL_PK, new String[] {linkIds[i].toString()}, true); + ContentProviderMediaExecuter.deleteMedia("DirectoryPickerFragment.fixLinks", context, FotoSql.FILTER_COL_PK, new String[]{linkIds[i].toString()}, true); } } PhotoPropertiesMediaFilesScanner.notifyChanges(context, "Fixed link/canonical duplicates"); 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 24a0f11b..224b8732 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 @@ -71,6 +71,7 @@ import de.k3b.android.androFotoFinder.imagedetail.ImageDetailMetaDialogBuilder; import de.k3b.android.androFotoFinder.locationmap.GeoEditActivity; import de.k3b.android.androFotoFinder.locationmap.MapGeoPickerActivity; +import de.k3b.android.androFotoFinder.queries.ContentProviderMediaExecuter; import de.k3b.android.androFotoFinder.queries.FotoSql; import de.k3b.android.androFotoFinder.queries.FotoViewerParameter; import de.k3b.android.androFotoFinder.queries.Queryable; @@ -1416,7 +1417,7 @@ private void onDuplicatesFound(SelectedItems selectedItems, StringBuffer debugMe String sqlWhere = query.toAndroidWhere(); // + " OR " + FotoSql.SQL_COL_PATH + " is null"; try { - delCount = FotoSql.deleteMedia(mDebugPrefix + "onDuplicatesFound", activity, sqlWhere, null, true); + delCount = ContentProviderMediaExecuter.deleteMedia(mDebugPrefix + "onDuplicatesFound", activity, sqlWhere, null, true); } catch (Exception ex) { Log.w(Global.LOG_CONTEXT, "deleteMedia via update failed for 'where " + sqlWhere + "'."); diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java index c6e1c239..880d922d 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/imagedetail/ImageDetailMetaDialogBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018 by k3b. + * Copyright (c) 2015-2019 by k3b. * * This file is part of AndroFotoFinder. * @@ -33,13 +33,14 @@ import java.util.Date; import java.util.List; +import de.k3b.android.androFotoFinder.queries.ContentProviderMediaExecuter; import de.k3b.android.androFotoFinder.tagDB.TagSql; import de.k3b.android.widget.ActivityWithCallContext; +import de.k3b.database.QueryParameter; import de.k3b.io.DateUtil; +import de.k3b.io.FileCommands; import de.k3b.media.ExifInterfaceEx; -import de.k3b.database.QueryParameter; import de.k3b.media.PhotoPropertiesImageReader; -import de.k3b.io.FileCommands; import de.k3b.media.XmpSegment; /** @@ -137,7 +138,7 @@ private static void appendExifInfo(StringBuilder result, Activity context, Strin if (currentImageId != 0) { - ContentValues dbContent = TagSql.getDbContent(context, currentImageId); + ContentValues dbContent = ContentProviderMediaExecuter.getDbContent(context, currentImageId); if (dbContent != null) { result.append(NL).append(line).append(NL); result.append(NL).append(TagSql.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE).append(NL).append(NL); diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/LocationMapFragment.java b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/LocationMapFragment.java index 75d9954d..7abacfe5 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/LocationMapFragment.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/LocationMapFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018 by k3b. + * Copyright (c) 2015-2019 by k3b. * * This file is part of AndroFotoFinder / #APhotoManager. * diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/MarkerLoaderTask.java b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/MarkerLoaderTask.java index b450f46b..8dae8276 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/MarkerLoaderTask.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/locationmap/MarkerLoaderTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 by k3b. + * Copyright (c) 2015-2019 by k3b. * * This file is part of AndroFotoFinder / #APhotoManager. * @@ -22,7 +22,6 @@ import android.app.Activity; import android.database.Cursor; import android.graphics.drawable.BitmapDrawable; -import android.net.Uri; import android.os.AsyncTask; import android.util.Log; @@ -34,9 +33,10 @@ import de.k3b.android.androFotoFinder.Global; import de.k3b.android.androFotoFinder.R; +import de.k3b.android.androFotoFinder.queries.ContentProviderMediaExecuter; import de.k3b.android.androFotoFinder.queries.FotoSql; -import de.k3b.android.osmdroid.IconFactory; import de.k3b.android.osmdroid.ClickableIconOverlay; +import de.k3b.android.osmdroid.IconFactory; import de.k3b.android.util.ResourceUtils; import de.k3b.database.QueryParameter; @@ -96,8 +96,9 @@ protected OverlayManager doInBackground(QueryParameter... queryParameter) { Cursor cursor = null; try { - cursor = mContext.getContentResolver().query(Uri.parse(queryParameters.toFrom()), queryParameters.toColumns(), - queryParameters.toAndroidWhere(), queryParameters.toAndroidParameters(), queryParameters.toOrderBy()); + cursor = ContentProviderMediaExecuter.createCursorForQuery( + null, "MakerLoader", mContext, + queryParameters, null); int itemCount = cursor.getCount(); final int expectedCount = itemCount + itemCount; diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/AndroidAlbumUtils.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/AndroidAlbumUtils.java index 5a4e38f6..9bceb428 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/queries/AndroidAlbumUtils.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/AndroidAlbumUtils.java @@ -368,7 +368,7 @@ public static void insertToMediaDB(String dbgContext, @NonNull Context context, ContentValues values = new ContentValues(); String newAbsolutePath = PhotoPropertiesMediaFilesScanner.setFileFields(values, fileToBeScannedAndInserted); values.put(FotoSql.SQL_COL_EXT_MEDIA_TYPE, FotoSql.MEDIA_TYPE_ALBUM_FILE); - FotoSql.insertOrUpdateMediaDatabase(dbgContext, context, newAbsolutePath, values, null, 1l); + ContentProviderMediaExecuter.insertOrUpdateMediaDatabase(dbgContext, context, newAbsolutePath, values, null, 1l); } } diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/ContentProviderMediaExecuter.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/ContentProviderMediaExecuter.java new file mode 100644 index 00000000..7eee65be --- /dev/null +++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/ContentProviderMediaExecuter.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2015-2019 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 + */ +package de.k3b.android.androFotoFinder.queries; + +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.util.Log; + +import java.util.ArrayList; + +import de.k3b.LibGlobal; +import de.k3b.android.androFotoFinder.Global; +import de.k3b.database.QueryParameter; +import de.k3b.io.StringUtils; +import de.k3b.io.VISIBILITY; + +public class ContentProviderMediaExecuter { + public static Cursor createCursorForQuery( + StringBuilder out_debugMessage, String dbgContext, final Context context, + QueryParameter parameters, VISIBILITY visibility) { + if (visibility != null) FotoSql.setWhereVisibility(parameters, visibility); + return createCursorForQuery(out_debugMessage, dbgContext, context, parameters.toFrom(), + parameters.toAndroidWhere(), + parameters.toAndroidParameters(), parameters.toOrderBy(), + parameters.toColumns() + ); + } + + /** + * every cursor query should go through this. adds logging if enabled + */ + static Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgContext, final Context context, final String from, final String sqlWhereStatement, + final String[] sqlWhereParameters, final String sqlSortOrder, + final String... sqlSelectColums) { + ContentResolver resolver = context.getContentResolver(); + Cursor query = null; + + Exception excpetion = null; + try { + query = resolver.query(Uri.parse(from), sqlSelectColums, sqlWhereStatement, sqlWhereParameters, sqlSortOrder); + } catch (Exception ex) { + excpetion = ex; + } finally { + if ((excpetion != null) || Global.debugEnabledSql || (out_debugMessage != null)) { + StringBuilder message = StringUtils.appendMessage(out_debugMessage, excpetion, + dbgContext, "FotoSql.createCursorForQuery:\n", + QueryParameter.toString(sqlSelectColums, null, from, sqlWhereStatement, + sqlWhereParameters, sqlSortOrder, query.getCount())); + if (out_debugMessage == null) { + Log.i(Global.LOG_CONTEXT, message.toString(), excpetion); + } // else logging is done by caller + } + } + + return query; + } + + public static int execUpdate(String dbgContext, Context context, long id, ContentValues values) { + return exexUpdateImpl(dbgContext, context, values, FotoSql.FILTER_COL_PK, new String[]{Long.toString(id)}); + } + + public static int execUpdate(String dbgContext, Context context, String path, ContentValues values, VISIBILITY visibility) { + return exexUpdateImpl(dbgContext, context, values, FotoSql.getFilterExprPathLikeWithVisibility(visibility), new String[]{path}); + } + + /** + * every database update should go through this. adds logging if enabled + */ + public static int exexUpdateImpl(String dbgContext, Context context, ContentValues values, String sqlWhere, String[] selectionArgs) { + int result = -1; + Exception excpetion = null; + try { + result = context.getContentResolver().update(FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, + values, sqlWhere, + selectionArgs); + } catch (Exception ex) { + excpetion = ex; + } finally { + if ((excpetion != null) || ((dbgContext != null) && (Global.debugEnabledSql || LibGlobal.debugEnabledJpg))) { + Log.i(Global.LOG_CONTEXT, dbgContext + ":FotoSql.exexUpdate " + excpetion + "\n" + + QueryParameter.toString(null, values.toString(), FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, + sqlWhere, selectionArgs, null, result), excpetion); + } + } + return result; + } + + /** + * return id of inserted item + */ + public static Long insertOrUpdateMediaDatabase(String dbgContext, Context context, + String dbUpdateFilterJpgFullPathName, + ContentValues values, VISIBILITY visibility, + Long updateSuccessValue) { + Long result = updateSuccessValue; + + int modifyCount = execUpdate(dbgContext, context, dbUpdateFilterJpgFullPathName, + values, visibility); + + if (modifyCount == 0) { + // update failed (probably becauce oldFullPathName not found. try insert it. + FotoSql.addDateAdded(values); + + Uri uriWithId = execInsert(dbgContext, context, values); + result = FotoSql.getId(uriWithId); + } + return result; + } + + /** + * every database insert should go through this. adds logging if enabled + */ + public static Uri execInsert(String dbgContext, Context context, ContentValues values) { + Uri providerUri = (null != values.get(FotoSql.SQL_COL_EXT_MEDIA_TYPE)) ? FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE : FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI; + + Uri result = null; + Exception excpetion = null; + try { + // on my android-4.4 insert with media_type=1001 (private) does insert with media_type=1 (image) + result = context.getContentResolver().insert(providerUri, values); + } catch (Exception ex) { + excpetion = ex; + } finally { + if ((excpetion != null) || Global.debugEnabledSql || LibGlobal.debugEnabledJpg) { + Log.i(Global.LOG_CONTEXT, dbgContext + ":FotoSql.execInsert " + excpetion + " " + + values.toString() + " => " + result + " " + excpetion, excpetion); + } + } + return result; + } + + /** + * Deletes media items specified by where with the option to prevent cascade delete of the image. + */ + public static int deleteMedia(String dbgContext, Context context, String where, String[] selectionArgs, boolean preventDeleteImageFile) { + String[] lastSelectionArgs = selectionArgs; + String lastUsedWhereClause = where; + int delCount = 0; + try { + if (preventDeleteImageFile) { + // set SQL_COL_PATH empty so sql-delete cannot cascade delete the referenced image-file via delete trigger + ContentValues values = new ContentValues(); + values.put(FotoSql.SQL_COL_PATH, FotoSql.DELETED_FILE_MARKER); + values.put(FotoSql.SQL_COL_EXT_MEDIA_TYPE, 0); // so it will not be shown as image any more + exexUpdateImpl(dbgContext + "-a: FotoSql.deleteMedia: ", + context, values, lastUsedWhereClause, lastSelectionArgs); + + lastUsedWhereClause = FotoSql.SQL_COL_PATH + " is null"; + lastSelectionArgs = null; + delCount = context.getContentResolver() + .delete(FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, lastUsedWhereClause, lastSelectionArgs); + if (Global.debugEnabledSql || LibGlobal.debugEnabledJpg) { + Log.i(Global.LOG_CONTEXT, dbgContext + "-b: FotoSql.deleteMedia delete\n" + + QueryParameter.toString(null, null, FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, + lastUsedWhereClause, lastSelectionArgs, null, delCount)); + } + } else { + delCount = context.getContentResolver() + .delete(FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, lastUsedWhereClause, lastSelectionArgs); + if (Global.debugEnabledSql || LibGlobal.debugEnabledJpg) { + Log.i(Global.LOG_CONTEXT, dbgContext + ": FotoSql.deleteMedia\ndelete " + + QueryParameter.toString(null, null, + FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, + lastUsedWhereClause, lastSelectionArgs, null, delCount)); + } + } + } catch (Exception ex) { + // null pointer exception when delete matches not items?? + final String msg = dbgContext + ": Exception in FotoSql.deleteMedia:\n" + + QueryParameter.toString(null, null, FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, + lastUsedWhereClause, lastSelectionArgs, null, -1) + + " : " + ex.getMessage(); + Log.e(Global.LOG_CONTEXT, msg, ex); + + } + return delCount; + } + + /** + * execRenameFolder(getActivity(),"/storage/sdcard0/testFolder/", "/storage/sdcard0/renamedFolder/") + * "/storage/sdcard0/testFolder/image.jpg" becomes "/storage/sdcard0/renamedFolder/image.jpg" + * + * @return number of updated items + */ + private static int _del_execRenameFolder_batch_not_working(Context context, String pathOld, String pathNew) { + final String dbgContext = "FotoSql.execRenameFolder('" + + pathOld + "' => '" + pathNew + "')"; + // sql update file set path = newBegin + substing(path, begin+len) where path like newBegin+'%' + // public static final String SQL_EXPR_FOLDER = "substr(" + SQL_COL_PATH + ",1,length(" + SQL_COL_PATH + ") - length(" + MediaStore.Images.Media.DISPLAY_NAME + "))"; + + final String sqlColNewPathAlias = "new_path"; + final String sql_col_pathnew = "'" + pathNew + "' || substr(" + FotoSql.SQL_COL_PATH + + "," + (pathOld.length() + 1) + ",255) AS " + sqlColNewPathAlias; + + QueryParameter queryAffectedFiles = new QueryParameter() + .setID(FotoSql.QUERY_TYPE_DEFAULT) + .addColumn(FotoSql.SQL_COL_PK, + sql_col_pathnew) + .addFrom(FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME) + .addWhere(FotoSql.SQL_COL_PATH + " like '" + pathOld + "%'") + // SQL_COL_EXT_MEDIA_TYPE IS NOT NULL enshures that all media types (mp3, mp4, txt,...) are updated + .addWhere(FotoSql.SQL_COL_EXT_MEDIA_TYPE + " IS NOT NULL"); + + ArrayList ops = new ArrayList(); + + Cursor c = null; + try { + c = createCursorForQuery(null, dbgContext, context, queryAffectedFiles, null); + int pkColNo = c.getColumnIndex(FotoSql.SQL_COL_PK); + int pathColNo = c.getColumnIndex(sqlColNewPathAlias); + + while (c.moveToNext()) { + // paths[row] = c.getString(pathColNo); + // ids[row] = c.getLong(pkColNo); + ops.add(ContentProviderOperation.newUpdate(FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE) + .withSelection(FotoSql.FILTER_COL_PK, new String[]{c.getString(pkColNo)}) + .withValue(FotoSql.SQL_COL_PATH, c.getString(pathColNo)) + .build()); + } + } catch (Exception ex) { + Log.e(Global.LOG_CONTEXT, dbgContext + "-getAffected error :", ex); + return -1; + } finally { + if (c != null) c.close(); + } + + try { + context.getContentResolver().applyBatch(FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, ops); + } catch (Exception ex) { + // java.lang.IllegalArgumentException: Unknown authority content://media/external/file + // i assume not batch support for file + Log.e(Global.LOG_CONTEXT, dbgContext + "-updateAffected error :", ex); + return -1; + } + return ops.size(); + } + + public static ContentValues getDbContent(Context context, final long id) { + ContentResolver resolver = context.getContentResolver(); + + Cursor c = null; + try { + c = resolver.query(FotoSqlBase.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, new String[]{"*"}, FotoSql.FILTER_COL_PK, new String[]{"" + id}, null); + if (c.moveToNext()) { + ContentValues values = new ContentValues(); + DatabaseUtils.cursorRowToContentValues(c, values); + return values; + } + } catch (Exception ex) { + Log.e(Global.LOG_CONTEXT, "FotoSql.getDbContent(id=" + id + ") failed", ex); + } finally { + if (c != null) c.close(); + } + return null; + } +} diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/DatabaseHelper.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/DatabaseHelper.java index 12327a67..f33f5ae7 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/queries/DatabaseHelper.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/DatabaseHelper.java @@ -19,7 +19,6 @@ package de.k3b.android.androFotoFinder.queries; -import android.app.Activity; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; @@ -37,13 +36,21 @@ */ public class DatabaseHelper extends SQLiteOpenHelper { public static final int DATABASE_VERSION_1_TransactionLog = 1; + public static final int DATABASE_VERSION_2_MEDIA_DB_COPY = 2; - public static final int DATABASE_VERSION = DatabaseHelper.DATABASE_VERSION_1_TransactionLog; + public static final int DATABASE_VERSION = DatabaseHelper.DATABASE_VERSION_2_MEDIA_DB_COPY; public DatabaseHelper(final Context context, final String databaseName) { super(context, databaseName, null, DatabaseHelper.DATABASE_VERSION); } + public static SQLiteDatabase getWritableDatabase(Context context) { + if (instance == null) { + instance = new DatabaseHelper(new DatabaseContext(context), "APhotoManager"); + } + return instance.getWritableDatabase(); + } + /** * called if database doesn-t exist yet */ @@ -51,7 +58,7 @@ public DatabaseHelper(final Context context, final String databaseName) { public void onCreate(final SQLiteDatabase db) { db.execSQL(TransactionLogSql.CREATE_TABLE); - this.version3Upgrade_TIMESLICE_WITH_NOTES(db); + this.version2Upgrade_MediDbCopy(db); } @Override @@ -59,24 +66,16 @@ public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { Log.w(this.getClass().toString(), "Upgrading database from version " + oldVersion + " to " + newVersion + ". (Old data is kept.)"); - if (oldVersion < DatabaseHelper.DATABASE_VERSION_1_TransactionLog) { - this.version3Upgrade_TIMESLICE_WITH_NOTES(db); + if (oldVersion < DatabaseHelper.DATABASE_VERSION_2_MEDIA_DB_COPY) { + this.version2Upgrade_MediDbCopy(db); } } - private void version3Upgrade_TIMESLICE_WITH_NOTES(final SQLiteDatabase db) { - // added timeslice.notes - /* - db.execSQL("ALTER TABLE " + TransactionLogSql.TABLE - + " ADD COLUMN " + TransactionLogSql.COL_NOTES + " TEXT"); - */ - } - private static DatabaseHelper instance = null; - public static SQLiteDatabase getWritableDatabase(Activity context) { - if (instance == null) { - instance = new DatabaseHelper(new DatabaseContext(context), "APhotoManager"); + + private void version2Upgrade_MediDbCopy(final SQLiteDatabase db) { + for (String sql : MediaImageDbReplacement.DDL) { + db.execSQL(sql); } - return instance.getWritableDatabase(); } } diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoSql.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoSql.java index f5e54f5a..a725ba0f 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoSql.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoSql.java @@ -20,8 +20,6 @@ package de.k3b.android.androFotoFinder.queries; import android.app.Activity; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.CursorLoader; @@ -108,6 +106,7 @@ public class FotoSql extends FotoSqlBase { public static final String SQL_COL_DISPLAY_TEXT = "disp_txt"; public static final String SQL_COL_LAT = MediaStore.Images.Media.LATITUDE; public static final String SQL_COL_LON = MediaStore.Images.Media.LONGITUDE; + public static final String SQL_COL_EXT_TITLE = MediaStore.Images.Media.TITLE; // new col id for with since ver 0.6.3 public static final String SQL_COL_WIDTH = "col_width"; @@ -133,10 +132,12 @@ public class FotoSql extends FotoSqlBase { // either code 0..8 or rotation angle 0, 90, 180, 270 public static final String SQL_COL_ORIENTATION = MediaStore.Images.ImageColumns.ORIENTATION; + public static final String SQL_COL__IMPL_DISPLAY_NAME = MediaStore.Images.Media.DISPLAY_NAME; + // only works with api >= 16 public static final String SQL_COL_MAX_WITH = - // #155: android10 incompatibility: check if the sqLite-max() function is the problem - (false && (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)) + + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "max(" + MediaStore.Images.Media.WIDTH + "," + MediaStore.Images.Media.HEIGHT +")" : "1024"; @@ -153,7 +154,7 @@ public class FotoSql extends FotoSqlBase { protected static final String FILTER_EXPR_PRIVATE = "(" + SQL_COL_EXT_MEDIA_TYPE + " = " + MEDIA_TYPE_IMAGE_PRIVATE + ")"; - protected static final String FILTER_EXPR_PRIVATE_PUBLIC + public static final String FILTER_EXPR_PRIVATE_PUBLIC = "(" + SQL_COL_EXT_MEDIA_TYPE + " in (" + MEDIA_TYPE_IMAGE_PRIVATE + "," + MEDIA_TYPE_IMAGE +"))"; protected static final String FILTER_EXPR_PUBLIC = "(" + SQL_COL_EXT_MEDIA_TYPE + " = " + MEDIA_TYPE_IMAGE + ")"; @@ -205,7 +206,7 @@ public class FotoSql extends FotoSqlBase { .addGroupBy(SQL_EXPR_DAY_MODIFIED) .addOrderBy(SQL_EXPR_DAY_MODIFIED); - public static final String SQL_EXPR_FOLDER = "substr(" + SQL_COL_PATH + ",1,length(" + SQL_COL_PATH + ") - length(" + MediaStore.Images.Media.DISPLAY_NAME + "))"; + public static final String SQL_EXPR_FOLDER = "substr(" + SQL_COL_PATH + ",1,length(" + SQL_COL_PATH + ") - length(" + SQL_COL__IMPL_DISPLAY_NAME + "))"; public static final QueryParameter queryGroupByDir = new QueryParameter() .setID(QUERY_TYPE_GROUP_ALBUM) .addColumn( @@ -267,7 +268,7 @@ public class FotoSql extends FotoSqlBase { /** to avoid cascade delete of linked file when mediaDB-item is deleted * the links are first set to null before delete. */ - private static final String DELETED_FILE_MARKER = null; + public static final String DELETED_FILE_MARKER = null; /** * translate from bytes to kilobytes @@ -707,7 +708,7 @@ public static QueryParameter setSort(QueryParameter result, int sortID, boolean case SORT_BY_LOCATION_OLD: case SORT_BY_LOCATION: - return result.replaceOrderBy(SQL_COL_GPS + asc, MediaStore.Images.Media.LATITUDE + asc); + return result.replaceOrderBy(SQL_COL_GPS + asc, SQL_COL_LAT + asc); case SORT_BY_NAME_LEN_OLD: case SORT_BY_NAME_LEN: return result.replaceOrderBy("length(" + SQL_COL_PATH + ")" + asc, SQL_COL_PATH + asc); @@ -757,7 +758,7 @@ public static boolean set(GalleryFilterParameter dest, String selectedAbsolutePa public static String execGetFotoPath(Context context, Uri uriWithID) { Cursor c = null; try { - c = createCursorForQuery(null, "execGetFotoPath(uri)", context, uriWithID.toString(), null, null, null, FotoSql.SQL_COL_PATH); + c = ContentProviderMediaExecuter.createCursorForQuery(null, "execGetFotoPath(uri)", context, uriWithID.toString(), null, null, null, FotoSql.SQL_COL_PATH); if (c.moveToFirst()) { return DBUtils.getString(c,FotoSql.SQL_COL_PATH, null); } @@ -775,7 +776,7 @@ public static List execGetFotoPaths(Context context, String pathFilter) Cursor c = null; try { - c = createCursorForQuery(null, "execGetFotoPaths(pathFilter)", context,SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, + c = ContentProviderMediaExecuter.createCursorForQuery(null, "execGetFotoPaths(pathFilter)", context, SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, FotoSql.SQL_COL_PATH + " like ? and " + FILTER_EXPR_PRIVATE_PUBLIC, new String[]{pathFilter}, FotoSql.SQL_COL_PATH, FotoSql.SQL_COL_PATH); while (c.moveToNext()) { @@ -793,43 +794,6 @@ public static List execGetFotoPaths(Context context, String pathFilter) return result; } - public static Cursor createCursorForQuery( - StringBuilder out_debugMessage, String dbgContext, final Context context, - QueryParameter parameters, VISIBILITY visibility) { - if (visibility != null) setWhereVisibility(parameters, visibility); - return createCursorForQuery(out_debugMessage, dbgContext, context, parameters.toFrom(), parameters.toAndroidWhere(), - parameters.toAndroidParameters(), parameters.toOrderBy(), - parameters.toColumns() - ); - } - - /** every cursor query should go through this. adds logging if enabled */ - private static Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgContext, final Context context, final String from, final String sqlWhereStatement, - final String[] sqlWhereParameters, final String sqlSortOrder, - final String... sqlSelectColums) { - ContentResolver resolver = context.getContentResolver(); - Cursor query = null; - - Exception excpetion = null; - try { - query = resolver.query(Uri.parse(from), sqlSelectColums, sqlWhereStatement, sqlWhereParameters, sqlSortOrder); - } catch (Exception ex) { - excpetion = ex; - } finally { - if ((excpetion != null) || Global.debugEnabledSql || (out_debugMessage != null)) { - StringBuilder message = StringUtils.appendMessage(out_debugMessage, excpetion, - dbgContext,"FotoSql.createCursorForQuery:\n" , - QueryParameter.toString(sqlSelectColums, null, from, sqlWhereStatement, - sqlWhereParameters, sqlSortOrder, query.getCount())); - if (out_debugMessage == null) { - Log.i(Global.LOG_CONTEXT, message.toString(), excpetion); - } // else logging is done by caller - } - } - - return query; - } - public static IGeoRectangle execGetGeoRectangle(StringBuilder out_debugMessage, Context context, QueryParameter baseQuery, SelectedItems selectedItems, Object... dbgContext) { StringBuilder debugMessage = (out_debugMessage == null) @@ -859,7 +823,7 @@ public static IGeoRectangle execGetGeoRectangle(StringBuilder out_debugMessage, GeoRectangle result = null; Cursor c = null; try { - c = createCursorForQuery(debugMessage, "execGetGeoRectangle", context, query, VISIBILITY.PRIVATE_PUBLIC); + c = ContentProviderMediaExecuter.createCursorForQuery(debugMessage, "execGetGeoRectangle", context, query, VISIBILITY.PRIVATE_PUBLIC); if (c.moveToFirst()) { result = new GeoRectangle(); result.setLatitude(c.getDouble(0), c.getDouble(1)); @@ -903,7 +867,7 @@ public static IGeoPoint execGetPosition(StringBuilder out_debugMessage, Context GeoPoint result = null; Cursor c = null; try { - c = createCursorForQuery(debugMessage, "execGetPosition", context, query, VISIBILITY.PRIVATE_PUBLIC); + c = ContentProviderMediaExecuter.createCursorForQuery(debugMessage, "execGetPosition", context, query, VISIBILITY.PRIVATE_PUBLIC); if (c.moveToFirst()) { result = new GeoPoint(c.getDouble(0),c.getDouble(1)); return result; @@ -938,7 +902,7 @@ public static Map execGetPathIdMap(Context context, String... file Cursor c = null; try { - c = createCursorForQuery(null, "execGetPathIdMap", context, query, null); + c = ContentProviderMediaExecuter.createCursorForQuery(null, "execGetPathIdMap", context, query, null); while (c.moveToNext()) { result.put(c.getString(1),c.getLong(0)); } @@ -972,14 +936,6 @@ public static String getWhereInFileNames(String... fileNames) { return null; } - public static int execUpdate(String dbgContext, Context context, long id, ContentValues values) { - return exexUpdateImpl(dbgContext, context, values, FILTER_COL_PK, new String[]{Long.toString(id)}); - } - - public static int execUpdate(String dbgContext, Context context, String path, ContentValues values, VISIBILITY visibility) { - return exexUpdateImpl(dbgContext, context, values, getFilterExprPathLikeWithVisibility(visibility), new String[]{path}); - } - /** * execRenameFolder(getActivity(),"/storage/sdcard0/testFolder/", "/storage/sdcard0/renamedFolder/") * "/storage/sdcard0/testFolder/image.jpg" becomes "/storage/sdcard0/renamedFolder/image.jpg" @@ -1016,91 +972,13 @@ public static int execRenameFolder(Context context, String pathOld, String pathN for (int i = 0; i < ids.length; i++) { values.put(SQL_COL_PATH, paths[i]); selectionArgs[0] = ids[i].toString(); - if (exexUpdateImpl(_dbgContext, context, values, FILTER_COL_PK, selectionArgs) < 0) return -1; + if (ContentProviderMediaExecuter.exexUpdateImpl(_dbgContext, context, values, FILTER_COL_PK, selectionArgs) < 0) + return -1; _dbgContext = null; } return ids.length; } - /** - * execRenameFolder(getActivity(),"/storage/sdcard0/testFolder/", "/storage/sdcard0/renamedFolder/") - * "/storage/sdcard0/testFolder/image.jpg" becomes "/storage/sdcard0/renamedFolder/image.jpg" - * @return number of updated items - */ - private static int _del_execRenameFolder_batch_not_working(Context context, String pathOld, String pathNew) { - final String dbgContext = "FotoSql.execRenameFolder('" + - pathOld + "' => '" + pathNew + "')"; - // sql update file set path = newBegin + substing(path, begin+len) where path like newBegin+'%' - // public static final String SQL_EXPR_FOLDER = "substr(" + SQL_COL_PATH + ",1,length(" + SQL_COL_PATH + ") - length(" + MediaStore.Images.Media.DISPLAY_NAME + "))"; - - final String sqlColNewPathAlias = "new_path"; - final String sql_col_pathnew = "'" + pathNew + "' || substr(" + SQL_COL_PATH + - "," + (pathOld.length() + 1) + ",255) AS " + sqlColNewPathAlias; - - QueryParameter queryAffectedFiles = new QueryParameter() - .setID(QUERY_TYPE_DEFAULT) - .addColumn(SQL_COL_PK, - sql_col_pathnew) - .addFrom(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME) - .addWhere(SQL_COL_PATH + " like '" + pathOld + "%'") - // SQL_COL_EXT_MEDIA_TYPE IS NOT NULL enshures that all media types (mp3, mp4, txt,...) are updated - .addWhere(SQL_COL_EXT_MEDIA_TYPE + " IS NOT NULL") - ; - - ArrayList ops = new ArrayList(); - - Cursor c = null; - try { - c = createCursorForQuery(null, dbgContext, context, queryAffectedFiles, null); - int pkColNo = c.getColumnIndex(FotoSql.SQL_COL_PK); - int pathColNo = c.getColumnIndex(sqlColNewPathAlias); - - while (c.moveToNext()) { - // paths[row] = c.getString(pathColNo); - // ids[row] = c.getLong(pkColNo); - ops.add(ContentProviderOperation.newUpdate(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE) - .withSelection(FILTER_COL_PK, new String[]{c.getString(pkColNo)}) - .withValue(SQL_COL_PATH, c.getString(pathColNo)) - .build()); - } - } catch (Exception ex) { - Log.e(Global.LOG_CONTEXT, dbgContext + "-getAffected error :", ex); - return -1; - } finally { - if (c != null) c.close(); - } - - try { - context.getContentResolver().applyBatch(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, ops); - } catch (Exception ex) { - // java.lang.IllegalArgumentException: Unknown authority content://media/external/file - // i assume not batch support for file - Log.e(Global.LOG_CONTEXT, dbgContext + "-updateAffected error :", ex); - return -1; - } - return ops.size(); - } - - /** every database update should go through this. adds logging if enabled */ - protected static int exexUpdateImpl(String dbgContext, Context context, ContentValues values, String sqlWhere, String[] selectionArgs) { - int result = -1; - Exception excpetion = null; - try { - result = context.getContentResolver().update(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, - values, sqlWhere, - selectionArgs); - } catch (Exception ex) { - excpetion = ex; - } finally { - if ((excpetion != null) || ((dbgContext != null) && (Global.debugEnabledSql || LibGlobal.debugEnabledJpg))) { - Log.i(Global.LOG_CONTEXT, dbgContext + ":FotoSql.exexUpdate " + excpetion + "\n" + - QueryParameter.toString(null, values.toString(), SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, - sqlWhere, selectionArgs, null, result), excpetion); - } - } - return result; - } - protected static String getFilterExprPathLikeWithVisibility(VISIBILITY visibility) { // visibility VISIBILITY.PRIVATE_PUBLIC String resultExpression = FotoSql.FILTER_EXPR_PATH_LIKE; @@ -1110,46 +988,6 @@ protected static String getFilterExprPathLikeWithVisibility(VISIBILITY visibilit return resultExpression; } - /** return id of inserted item */ - public static Long insertOrUpdateMediaDatabase(String dbgContext, Context context, - String dbUpdateFilterJpgFullPathName, - ContentValues values, VISIBILITY visibility, - Long updateSuccessValue) { - Long result = updateSuccessValue; - - int modifyCount = FotoSql.execUpdate(dbgContext, context, dbUpdateFilterJpgFullPathName, - values, visibility); - - if (modifyCount == 0) { - // update failed (probably becauce oldFullPathName not found. try insert it. - FotoSql.addDateAdded(values); - - Uri uriWithId = FotoSql.execInsert(dbgContext, context, values); - result = getId(uriWithId); - } - return result; - } - - /** every database insert should go through this. adds logging if enabled */ - public static Uri execInsert(String dbgContext, Context context, ContentValues values) { - Uri providerUri = (null != values.get(SQL_COL_EXT_MEDIA_TYPE)) ? SQL_TABLE_EXTERNAL_CONTENT_URI_FILE : SQL_TABLE_EXTERNAL_CONTENT_URI; - - Uri result = null; - Exception excpetion = null; - try { - // on my android-4.4 insert with media_type=1001 (private) does insert with media_type=1 (image) - result = context.getContentResolver().insert(providerUri, values); - } catch (Exception ex) { - excpetion = ex; - } finally { - if ((excpetion != null) || Global.debugEnabledSql || LibGlobal.debugEnabledJpg) { - Log.i(Global.LOG_CONTEXT, dbgContext + ":FotoSql.execInsert " + excpetion + " " + - values.toString() + " => " + result + " " + excpetion, excpetion); - } - } - return result; - } - @NonNull public static CursorLoader createCursorLoader(Context context, final QueryParameter query) { FotoSql.setWhereVisibility(query, VISIBILITY.DEFAULT); @@ -1158,7 +996,7 @@ public static CursorLoader createCursorLoader(Context context, final QueryParame } public static int execDeleteByPath(String dbgContext, Activity context, String parentDirString, VISIBILITY visibility) { - int delCount = FotoSql.deleteMedia(dbgContext, context, getFilterExprPathLikeWithVisibility(visibility), new String[] {parentDirString + "/%"}, true); + int delCount = ContentProviderMediaExecuter.deleteMedia(dbgContext, context, getFilterExprPathLikeWithVisibility(visibility), new String[]{parentDirString + "/%"}, true); return delCount; } @@ -1166,7 +1004,7 @@ public static int deleteMedia(String dbgContext, Context context, List p boolean preventDeleteImageFile) { if ((pathsToBeRemoved != null) && (pathsToBeRemoved.size() > 0)) { String whereDelete = SQL_COL_PATH + " in ('" + ListUtils.toString("','", pathsToBeRemoved) + "')"; - return deleteMedia(dbgContext, context, whereDelete, null, preventDeleteImageFile); + return ContentProviderMediaExecuter.deleteMedia(dbgContext, context, whereDelete, null, preventDeleteImageFile); } return 0; } @@ -1185,57 +1023,10 @@ public static int deleteMediaWithNullPath(Context context) { QueryParameter whereInIds = new QueryParameter(); FotoSql.setWhereSelectionPks(whereInIds, pksAsString); - return deleteMedia("delete without path (_data = null)", context, whereInIds.toAndroidWhere(), null, true); + return ContentProviderMediaExecuter.deleteMedia("delete without path (_data = null)", context, whereInIds.toAndroidWhere(), null, true); } return 0; } - /** - * Deletes media items specified by where with the option to prevent cascade delete of the image. - */ - public static int deleteMedia(String dbgContext, Context context, String where, String[] selectionArgs, boolean preventDeleteImageFile) - { - String[] lastSelectionArgs = selectionArgs; - String lastUsedWhereClause = where; - int delCount = 0; - try { - if (preventDeleteImageFile) { - // set SQL_COL_PATH empty so sql-delete cannot cascade delete the referenced image-file via delete trigger - ContentValues values = new ContentValues(); - values.put(FotoSql.SQL_COL_PATH, DELETED_FILE_MARKER); - values.put(FotoSql.SQL_COL_EXT_MEDIA_TYPE, 0); // so it will not be shown as image any more - exexUpdateImpl(dbgContext + "-a: FotoSql.deleteMedia: ", - context, values, lastUsedWhereClause, lastSelectionArgs); - - lastUsedWhereClause = FotoSql.SQL_COL_PATH + " is null"; - lastSelectionArgs = null; - delCount = context.getContentResolver() - .delete(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, lastUsedWhereClause, lastSelectionArgs); - if (Global.debugEnabledSql || LibGlobal.debugEnabledJpg) { - Log.i(Global.LOG_CONTEXT, dbgContext + "-b: FotoSql.deleteMedia delete\n" + - QueryParameter.toString(null, null, SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, - lastUsedWhereClause, lastSelectionArgs, null, delCount)); - } - } else { - delCount = context.getContentResolver() - .delete(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, lastUsedWhereClause, lastSelectionArgs); - if (Global.debugEnabledSql || LibGlobal.debugEnabledJpg) { - Log.i(Global.LOG_CONTEXT, dbgContext +": FotoSql.deleteMedia\ndelete " + - QueryParameter.toString(null, null, - SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, - lastUsedWhereClause, lastSelectionArgs, null, delCount)); - } - } - } catch (Exception ex) { - // null pointer exception when delete matches not items?? - final String msg = dbgContext + ": Exception in FotoSql.deleteMedia:\n" + - QueryParameter.toString(null, null, SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME, - lastUsedWhereClause, lastSelectionArgs, null, -1) - + " : " + ex.getMessage(); - Log.e(Global.LOG_CONTEXT, msg, ex); - - } - return delCount; - } /** converts imageID to content-uri */ public static Uri getUri(long imageID) { @@ -1290,7 +1081,7 @@ public static String getMinFolder(Context context, QueryParameter query, Cursor c = null; try { - c = FotoSql.createCursorForQuery(null, "getCount", context, queryModified, null); + c = ContentProviderMediaExecuter.createCursorForQuery(null, "getCount", context, queryModified, null); if (c.moveToNext()) { return c.getString(0); } @@ -1309,7 +1100,7 @@ public static long getCount(Context context, QueryParameter query) { Cursor c = null; try { - c = FotoSql.createCursorForQuery(null, "getCount", context, queryModified, null); + c = ContentProviderMediaExecuter.createCursorForQuery(null, "getCount", context, queryModified, null); if (c.moveToNext()) { return c.getLong(0); } @@ -1338,7 +1129,7 @@ public static CharSequence getStatisticsMessage(Context context, int prefixStrin Cursor c = null; try { - c = FotoSql.createCursorForQuery(null, "getCount", context, queryModified, null); + c = ContentProviderMediaExecuter.createCursorForQuery(null, "getCount", context, queryModified, null); if (c.moveToNext()) { final long count = c.getLong(0); long size = c.getLong(1); @@ -1374,7 +1165,7 @@ private static SelectedFiles getSelectedfiles(Context context, QueryParameter qu Cursor c = null; try { - c = FotoSql.createCursorForQuery(null, "getSelectedfiles", context, query, visibility); + c = ContentProviderMediaExecuter.createCursorForQuery(null, "getSelectedfiles", context, query, visibility); int len = c.getCount(); Long[] ids = new Long[len]; String[] paths = new String[len]; @@ -1448,7 +1239,7 @@ private static List getFileNamesImpl(Context context, QueryParameter par Cursor cursor = null; try { - cursor = createCursorForQuery(null, "getFileNames", context, parameters, VISIBILITY.PRIVATE_PUBLIC); + cursor = ContentProviderMediaExecuter.createCursorForQuery(null, "getFileNames", context, parameters, VISIBILITY.PRIVATE_PUBLIC); int colPath = cursor.getColumnIndex(SQL_COL_DISPLAY_TEXT); if (colPath == -1) colPath = cursor.getColumnIndex(SQL_COL_PATH); @@ -1526,7 +1317,7 @@ public static List getAlbumFiles(Context context, String path, int subDi public static int execRename(Context context, String oldFullPath, String newFullPath) { ContentValues values = new ContentValues(); values.put(SQL_COL_PATH, newFullPath); - return FotoSql.execUpdate("rename file", context, oldFullPath, + return ContentProviderMediaExecuter.execUpdate("rename file", context, oldFullPath, values, null); } diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoThumbSql.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoThumbSql.java index 137eb258..b9b00c78 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoThumbSql.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/FotoThumbSql.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 by k3b. + * Copyright (c) 2015-2019 by k3b. * * This file is part of AndroFotoFinder / #APhotoManager. * @@ -66,7 +66,7 @@ private static String getStatistic(Context context, QueryParameter query, String Cursor c = null; try { - c = FotoSql.createCursorForQuery(null, mDebugPrefix + "getStatistic", context, query, VISIBILITY.PRIVATE_PUBLIC); + c = ContentProviderMediaExecuter.createCursorForQuery(null, mDebugPrefix + "getStatistic", context, query, VISIBILITY.PRIVATE_PUBLIC); if (Global.debugEnabledSql) { Log.i(Global.LOG_CONTEXT, mDebugPrefix + "getStatistic " + c.getCount() + "\n\t" + query.toSqlString()); diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaImageDbReplacement.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaImageDbReplacement.java new file mode 100644 index 00000000..4818a043 --- /dev/null +++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaImageDbReplacement.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2015-2019 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 + */ +package de.k3b.android.androFotoFinder.queries; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.provider.MediaStore; + +import java.sql.Date; + +import de.k3b.android.androFotoFinder.Global; +import de.k3b.database.QueryParameter; +import de.k3b.io.AlbumFile; + +import static de.k3b.android.androFotoFinder.queries.FotoSql.QUERY_TYPE_UNDEFINED; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_DATE_ADDED; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_DATE_TAKEN; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_EXT_MEDIA_TYPE; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_EXT_RATING; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_EXT_TITLE; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_LAST_MODIFIED; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_LAT; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_LON; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_ORIENTATION; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_PATH; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_PK; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL_SIZE; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_COL__IMPL_DISPLAY_NAME; +import static de.k3b.android.androFotoFinder.queries.FotoSql.SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_EXT_DESCRIPTION; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_EXT_TAGS; +import static de.k3b.android.androFotoFinder.tagDB.TagSql.SQL_COL_EXT_XMP_LAST_MODIFIED_DATE; + +/** + * Since Android-10 (api 29) using sqLite functions as content-provider-columns is not possible anymore. + * Therefore apm uses a copy of contentprovider MediaStore.Images with same column names. + */ +public class MediaImageDbReplacement { + /** + * 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[]{ + "CREATE TABLE \"files\" (\n" + + "\t_id INTEGER PRIMARY KEY AUTOINCREMENT,\n" + + "\t_size INTEGER,\n" + + "\tdate_added INTEGER,\n" + + "\tdate_modified INTEGER,\n" + + "\tdatetaken INTEGER,\n" + + "\torientation INTEGER,\n" + + "\tduration INTEGER,\n" + + "\tbookmark INTEGER,\n" + + "\tmedia_type INTEGER,\n" + + "\twidth INTEGER,\n" + + "\theight INTEGER,\n" + + + "\t_data TEXT UNIQUE COLLATE NOCASE,\n" + + "\ttitle TEXT,\n" + + "\tdescription TEXT,\n" + + "\t_display_name TEXT,\n" + + "\tmime_type TEXT,\n" + + "\ttags TEXT,\n" + + + "\tlatitude DOUBLE,\n" + + "\tlongitude DOUBLE\n" + + "\t )", + "CREATE INDEX media_type_index ON files(media_type)", + "CREATE INDEX path_index ON files(_data)", + "CREATE INDEX sort_index ON files(datetaken ASC, _id ASC)", + "CREATE INDEX title_idx ON files(title)", + "CREATE INDEX titlekey_index ON files(title_key)", + "CREATE INDEX media_type_index ON files(media_type)", + }; + + public static final String table = "files"; + // same colum order as in DDL + private static final String[] USED_MEDIA_COLUMNS = new String[]{ + // INTEGER 0 .. 10 + SQL_COL_PK, + SQL_COL_DATE_ADDED, + SQL_COL_LAST_MODIFIED, + SQL_COL_SIZE, + SQL_COL_DATE_TAKEN, + SQL_COL_ORIENTATION, + SQL_COL_EXT_XMP_LAST_MODIFIED_DATE, // duration + SQL_COL_EXT_RATING, // bookmark + SQL_COL_EXT_MEDIA_TYPE, + MediaStore.MediaColumns.WIDTH, + MediaStore.MediaColumns.HEIGHT, + + // TEXT 11 .. 16 + SQL_COL_PATH, // _data + SQL_COL_EXT_TITLE, + SQL_COL_EXT_DESCRIPTION, + SQL_COL__IMPL_DISPLAY_NAME, + MediaStore.MediaColumns.MIME_TYPE, + SQL_COL_EXT_TAGS, + + // DOUBLE 17..18 + SQL_COL_LAT, + SQL_COL_LON, + }; + + 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 + "' " + + " OR " + SQL_COL_PATH + " like '%" + AlbumFile.SUFFIX_QUERY + "' " + + ")"; + private static final QueryParameter queryGetAllColumns = new QueryParameter() + .setID(QUERY_TYPE_UNDEFINED) + .addColumn(USED_MEDIA_COLUMNS) + .addFrom(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE_NAME) + .addWhere(FILTER_EXPR_AFFECTED_FILES); + + private static boolean isLomg(int index) { + return index >= intMin && index <= intMax; + } + + // private Object get(Cursor cursor, columIndex) + + private static boolean isString(int index) { + return index >= txtMin && index <= txtMax; + } + + private static boolean isDouble(int index) { + return index >= dblMin && index <= dblMax; + } + + private static ContentValues getContentValues(Cursor cursor, ContentValues destination) { + destination.clear(); + int colCount = cursor.getColumnCount(); + String columnName; + for (int i = 0; i < colCount; i++) { + columnName = cursor.getColumnName(i); + if (cursor.isNull(i)) { + destination.putNull(columnName); + } else if (isLomg(i)) { + destination.put(columnName, cursor.getLong(i)); + } else if (isString(i)) { + destination.put(columnName, cursor.getString(i)); + } else if (isDouble(i)) { + destination.put(columnName, cursor.getDouble(i)); + } + } + return destination; + } + + public static int updateMedaiCopy(Context context, SQLiteDatabase db, Date lastUpdate) { + int changeCount = 0; + + QueryParameter query = queryGetAllColumns; + long _lastUpdate = (lastUpdate != null) ? (lastUpdate.getTime() / 1000L) : 0L; + + if (_lastUpdate != 0) { + query = new QueryParameter().getFrom(queryGetAllColumns); + FotoSql.addWhereDateModifiedMinMax(query, _lastUpdate, 0); + // FotoSql.createCursorForQuery() + } + Cursor c = null; + ContentValues contentValues = new ContentValues(); + try { + c = ContentProviderMediaExecuter.createCursorForQuery(null, "execGetFotoPaths(pathFilter)", context, + query, null); + while (c.moveToNext()) { + getContentValues(c, contentValues); + save(db, c, contentValues, _lastUpdate); + } + } catch (Exception ex) { + // Log.e(Global.LOG_CONTEXT, "FotoSql.execGetFotoPaths() Cannot get path from: " + FotoSql.SQL_COL_PATH + " like '" + pathFilter +"'", ex); + } finally { + if (c != null) c.close(); + } + + if (Global.debugEnabled) { + // Log.d(Global.LOG_CONTEXT, "FotoSql.execGetFotoPaths() result count=" + result.size()); + } + return 0; + } + + 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/androFotoFinder/queries/SqlJobTaskBase.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/SqlJobTaskBase.java index 949bac19..4a580e90 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/queries/SqlJobTaskBase.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/SqlJobTaskBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017 by k3b. + * Copyright (c) 2015-2019 by k3b. * * This file is part of AndroFotoFinder / #APhotoManager. * @@ -21,7 +21,6 @@ import android.app.Activity; import android.database.Cursor; -import android.net.Uri; import android.os.AsyncTask; import android.util.Log; @@ -66,8 +65,9 @@ protected SelectedItems doInBackground(QueryParameter... querys) { Cursor cursor = null; try { - cursor = mContext.getContentResolver().query(Uri.parse(query.toFrom()), query.toColumns(), - query.toAndroidWhere(), query.toAndroidParameters(), query.toOrderBy()); + cursor = ContentProviderMediaExecuter.createCursorForQuery( + null, "SqlJobTask", mContext, + query, null); int itemCount = cursor.getCount(); final int expectedCount = itemCount + itemCount; diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagSql.java b/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagSql.java index 4dee2740..a9d7c4db 100644 --- a/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagSql.java +++ b/app/src/main/java/de/k3b/android/androFotoFinder/tagDB/TagSql.java @@ -19,11 +19,9 @@ package de.k3b.android.androFotoFinder.tagDB; -import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; -import android.database.DatabaseUtils; import android.provider.MediaStore; import android.util.Log; @@ -36,6 +34,7 @@ import de.k3b.android.androFotoFinder.Global; import de.k3b.android.androFotoFinder.media.PhotoPropertiesMediaDBContentValues; import de.k3b.android.androFotoFinder.queries.AndroidAlbumUtils; +import de.k3b.android.androFotoFinder.queries.ContentProviderMediaExecuter; import de.k3b.android.androFotoFinder.queries.FotoSql; import de.k3b.android.util.PhotoPropertiesMediaFilesScanner; import de.k3b.database.QueryParameter; @@ -61,7 +60,6 @@ public class TagSql extends FotoSql { public static final String SQL_COL_EXT_TAGS = MediaStore.Video.Media.TAGS; public static final String SQL_COL_EXT_DESCRIPTION = MediaStore.Images.Media.DESCRIPTION; - public static final String SQL_COL_EXT_TITLE = MediaStore.Images.Media.TITLE; /** The date & time when last non standard media-scan took place *

Type: INTEGER (long) as milliseconds since jan 1, 1970

*/ @@ -275,7 +273,7 @@ public static int fixPrivate(Context context) { PhotoPropertiesUtil.IMG_TYPE_PRIVATE + "'")); } where.append(")"); - return exexUpdateImpl("Fix visibility private", context, + return ContentProviderMediaExecuter.exexUpdateImpl("Fix visibility private", context, values, where.toString(), new String[] {"%;" + VISIBILITY.TAG_PRIVATE + ";%"}); } @@ -327,25 +325,6 @@ public static void setFileModifyDate(ContentValues values, long fileModifyDateSe } } - public static ContentValues getDbContent(Context context, final long id) { - ContentResolver resolver = context.getContentResolver(); - - Cursor c = null; - try { - c = resolver.query(SQL_TABLE_EXTERNAL_CONTENT_URI_FILE, new String[]{"*"}, FILTER_COL_PK, new String[]{"" + id}, null); - if (c.moveToNext()) { - ContentValues values = new ContentValues(); - DatabaseUtils.cursorRowToContentValues(c, values); - return values; - } - } catch (Exception ex) { - Log.e(Global.LOG_CONTEXT, "FotoSql.getDbContent(id=" + id + ") failed", ex); - } finally { - if (c != null) c.close(); - } - return null; - } - /** * Copies non null content of jpg to existing media database item. * @@ -391,9 +370,9 @@ public static int updateDB(String dbgContext, Context context, String oldFullJpg public static int execUpdate(String dbgContext, Context context, String path, long xmpFileDate, ContentValues values, VISIBILITY visibility) { if ((!Global.Media.enableXmpNone) || (xmpFileDate == EXT_LAST_EXT_SCAN_UNKNOWN)) { - return execUpdate(dbgContext, context, path, values, visibility); + return ContentProviderMediaExecuter.execUpdate(dbgContext, context, path, values, visibility); } - return exexUpdateImpl(dbgContext, context, values, FILTER_EXPR_PATH_LIKE_XMP_DATE, new String[]{path, Long.toString(xmpFileDate)}); + return ContentProviderMediaExecuter.exexUpdateImpl(dbgContext, context, values, FILTER_EXPR_PATH_LIKE_XMP_DATE, new String[]{path, Long.toString(xmpFileDate)}); } /** return how many photos exist that have one or more tags from list */ @@ -403,7 +382,7 @@ public static int getTagRefCount(Context context, List tags) { if (addWhereAnyOfTags(query, tags) > 0) { Cursor c = null; try { - c = createCursorForQuery(null, "getTagRefCount", context, query, VISIBILITY.PRIVATE_PUBLIC); + c = ContentProviderMediaExecuter.createCursorForQuery(null, "getTagRefCount", context, query, VISIBILITY.PRIVATE_PUBLIC); if (c.moveToFirst()) { return c.getInt(0); } @@ -457,7 +436,7 @@ public static List loadTagWorflowItems(Context context, String s if (filterCount > 0) { try { - c = createCursorForQuery(null, "loadTagWorflowItems", context, query, VISIBILITY.PRIVATE_PUBLIC); + c = ContentProviderMediaExecuter.createCursorForQuery(null, "loadTagWorflowItems", context, query, VISIBILITY.PRIVATE_PUBLIC); if (c.moveToFirst()) { do { result.add(new TagWorflowItem(c.getLong(0), c.getString(1), TagConverter.fromString(c.getString(2)), diff --git a/app/src/main/java/de/k3b/android/util/AndroidFileCommands.java b/app/src/main/java/de/k3b/android/util/AndroidFileCommands.java index 392ee504..72f04c3a 100644 --- a/app/src/main/java/de/k3b/android/util/AndroidFileCommands.java +++ b/app/src/main/java/de/k3b/android/util/AndroidFileCommands.java @@ -42,6 +42,7 @@ import de.k3b.android.androFotoFinder.R; import de.k3b.android.androFotoFinder.directory.DirectoryPickerFragment; import de.k3b.android.androFotoFinder.media.AndroidPhotoPropertiesBulkUpdateService; +import de.k3b.android.androFotoFinder.queries.ContentProviderMediaExecuter; import de.k3b.android.androFotoFinder.queries.DatabaseHelper; import de.k3b.android.androFotoFinder.queries.FotoSql; import de.k3b.android.androFotoFinder.tagDB.TagSql; @@ -328,7 +329,7 @@ public int deleteFiles(SelectedFiles fotos, IProgessListener progessListener) { QueryParameter where = new QueryParameter(); FotoSql.setWhereSelectionPks (where, fotos.toIdString()); - FotoSql.deleteMedia("AndroidFileCommands.deleteFiles", mContext, where.toAndroidWhere(), null, true); + ContentProviderMediaExecuter.deleteMedia("AndroidFileCommands.deleteFiles", mContext, where.toAndroidWhere(), null, true); } return deleteCount; } 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 8b620476..2388e523 100644 --- a/app/src/main/java/de/k3b/android/util/PhotoPropertiesMediaFilesScanner.java +++ b/app/src/main/java/de/k3b/android/util/PhotoPropertiesMediaFilesScanner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018 by k3b. + * Copyright (c) 2015-2019 by k3b. * * This file is part of AndroFotoFinder / #APhotoManager. * @@ -44,6 +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.ContentProviderMediaExecuter; import de.k3b.android.androFotoFinder.queries.FotoSql; import de.k3b.android.androFotoFinder.tagDB.TagSql; import de.k3b.database.QueryParameter; @@ -52,9 +53,9 @@ import de.k3b.io.FileUtils; import de.k3b.io.VISIBILITY; import de.k3b.media.IPhotoProperties; +import de.k3b.media.PhotoPropertiesChainReader; import de.k3b.media.PhotoPropertiesUtil; import de.k3b.media.PhotoPropertiesXmpSegment; -import de.k3b.media.PhotoPropertiesChainReader; import de.k3b.tagDB.TagRepository; /** @@ -75,15 +76,15 @@ abstract public class PhotoPropertiesMediaFilesScanner { protected static final String DB_LATITUDE = MediaStore.Images.Media.LATITUDE; */ - private static final String DB_SIZE = MediaStore.MediaColumns.SIZE; + protected static final String DB_TITLE = FotoSql.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_TITLE = MediaStore.MediaColumns.TITLE; + protected static final String DB_DATA = FotoSql.SQL_COL_PATH; // _data protected static final String DB_DISPLAY_NAME = MediaStore.MediaColumns.DISPLAY_NAME; - protected static final String DB_DATA = MediaStore.MediaColumns.DATA; + private static final String DB_SIZE = FotoSql.SQL_COL_SIZE; public static final int DEFAULT_SCAN_DEPTH = 22; public static final String MEDIA_IGNORE_FILENAME = FileUtils.MEDIA_IGNORE_FILENAME; // MediaStore.MEDIA_IGNORE_FILENAME; @@ -232,7 +233,7 @@ public Long insertOrUpdateMediaDatabase(String dbgContext, Context context, if ((currentJpgFile != null) && currentJpgFile.exists() && currentJpgFile.canRead()) { ContentValues values = createDefaultContentValues(); getExifFromFile(values, currentJpgFile); - Long result = FotoSql.insertOrUpdateMediaDatabase( + Long result = ContentProviderMediaExecuter.insertOrUpdateMediaDatabase( dbgContext, context, dbUpdateFilterJpgFullPathName, values, VISIBILITY.PRIVATE_PUBLIC, updateSuccessValue); @@ -248,7 +249,7 @@ private int deleteInMediaDatabase(Context context, String[] oldPathNames) { if ((oldPathNames != null) && (oldPathNames.length > 0)) { String sqlWhere = FotoSql.getWhereInFileNames(oldPathNames); try { - modifyCount = FotoSql.deleteMedia(CONTEXT + "deleteInMediaDatabase", context, sqlWhere, null, true); + modifyCount = ContentProviderMediaExecuter.deleteMedia(CONTEXT + "deleteInMediaDatabase", context, sqlWhere, null, true); if (Global.debugEnabled) { Log.d(Global.LOG_CONTEXT, CONTEXT + "deleteInMediaDatabase(len=" + oldPathNames.length + ", files='" + oldPathNames[0] + "'...) result count=" + modifyCount); } @@ -300,7 +301,7 @@ private int renameInMediaDatabase(Context context, Map old2NewFi Cursor c = null; try { - c = FotoSql.createCursorForQuery(null, "renameInMediaDatabase", context, query, VISIBILITY.PRIVATE_PUBLIC); + c = ContentProviderMediaExecuter.createCursorForQuery(null, "renameInMediaDatabase", context, query, VISIBILITY.PRIVATE_PUBLIC); int pkColNo = c.getColumnIndex(FotoSql.SQL_COL_PK); int pathColNo = c.getColumnIndex(FotoSql.SQL_COL_PATH); while (c.moveToNext()) { @@ -431,7 +432,7 @@ public int updatePathRelatedFields(Context context, Cursor cursor, String newAbs String oldAbsolutePath = cursor.getString(columnIndexPath); int id = cursor.getInt(columnIndexPk); setPathRelatedFieldsIfNeccessary(values, newAbsolutePath, oldAbsolutePath); - return FotoSql.execUpdate("updatePathRelatedFields", context, id, values); + return ContentProviderMediaExecuter.execUpdate("updatePathRelatedFields", context, id, values); } /** sets the path related fields */ @@ -463,7 +464,7 @@ private int update_Android42(String dbgContext, Context context, long id, File f if ((file != null) && file.exists() && file.canRead()) { ContentValues values = createDefaultContentValues(); getExifFromFile(values, file); - return FotoSql.execUpdate(dbgContext, context, id, values); + return ContentProviderMediaExecuter.execUpdate(dbgContext, context, id, values); } return 0; } @@ -487,7 +488,7 @@ private int insert_Android42(String dbgContext, Context context, File file) { FotoSql.addDateAdded(values); getExifFromFile(values, file); - return (null != FotoSql.execInsert(dbgContext, context, values)) ? 1 : 0; + return (null != ContentProviderMediaExecuter.execInsert(dbgContext, context, values)) ? 1 : 0; } return 0; }