From 6d7a3a47f1f1591623fe96a4eea59b5c20b0fe5d Mon Sep 17 00:00:00 2001
From: k3b <1374583+k3b@users.noreply.github.com>
Date: Sat, 17 Apr 2021 22:32:21 +0200
Subject: [PATCH] * #190: Backup/Restore lat/long/tags/rating in local DB
---
.../androFotoFinder/FotoGalleryActivity.java | 9 +++
.../queries/DatabaseHelper.java | 59 +++++++++++++++++--
.../queries/MediaContent2DBUpdateService.java | 15 ++++-
.../queries/MediaDBRepository.java | 39 ++++++++++--
app/src/main/res/menu/menu_gallery_ao10.xml | 7 +++
app/src/main/res/values-de/strings.xml | 1 +
app/src/main/res/values/strings.xml | 1 +
.../metadata/android/en-US/changelogs/49.txt | 1 +
8 files changed, 120 insertions(+), 12 deletions(-)
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/FotoGalleryActivity.java b/app/src/main/java/de/k3b/android/androFotoFinder/FotoGalleryActivity.java
index e6d12be3..daaefb9c 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/FotoGalleryActivity.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/FotoGalleryActivity.java
@@ -213,6 +213,11 @@ public boolean onOptionsItemSelected(MenuItem item) {
if (0 != onDbUpdateCommand(item))
notifyPhotoChanged();
return true;
+
+ case R.id.cmd_db_backup:
+ onDbBackup();
+ return true;
+
case R.id.cmd_more:
new Handler().postDelayed(new Runnable() {
public void run() {
@@ -227,6 +232,10 @@ public void run() {
}
+ private void onDbBackup() {
+ AndroFotoFinderApp.getMediaContent2DbUpdateService().createBackup();
+ }
+
private int onDbUpdateCommand(MenuItem item) {
Activity activity = this;
int count = AndroFotoFinderApp.getMediaContent2DbUpdateService().update(this, 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 14653642..307cbf80 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
@@ -20,15 +20,19 @@
package de.k3b.android.androFotoFinder.queries;
import android.content.Context;
+import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.io.File;
+import de.k3b.android.androFotoFinder.Global;
import de.k3b.android.androFotoFinder.transactionlog.TransactionLogSql;
import de.k3b.android.util.DatabaseContext;
+import static de.k3b.android.androFotoFinder.queries.FotoSql.LOG_TAG;
+
/**
* Created by k3b on 22.02.2017.
*/
@@ -68,8 +72,53 @@ private static DatabaseHelper getInstance(Context context) {
}
- public static void version2Upgrade_RecreateMediDbCopy(final SQLiteDatabase db) {
- for (String sql : MediaDBRepository.Impl.DDL) {
+ public static void version2Upgrade_ReCreateMediaDbTable(final SQLiteDatabase db) {
+ execSql(db, "(Re)CreateMediaDbTable:", MediaDBRepository.Impl.DDL);
+ }
+
+ public static void createBackup(SQLiteDatabase db) {
+ if (tableExists(db, MediaDBRepository.Impl.DATABASE_TABLE_NAME)) {
+ // see https://www.techonthenet.com/sqlite/tables/create_table_as.php
+
+ if (!tableExists(db, MediaDBRepository.Impl.DATABASE_TABLE_NAME_BACKUP)) {
+ execSql(db, "create Backup:", MediaDBRepository.Impl.CREATE_BACKUP);
+ } else {
+ execSql(db, "update Backup:", MediaDBRepository.Impl.UPDATE_BACKUP);
+ }
+ }
+ }
+
+ public static void restoreFromBackup(SQLiteDatabase db) {
+ if (tableExists(db, MediaDBRepository.Impl.DATABASE_TABLE_NAME_BACKUP)) {
+ // see https://stackoverflow.com/questions/19270259/update-with-join-in-sqlite
+ execSql(db, "restoreFromBackup:", MediaDBRepository.Impl.RESTORE_FROM_BACKUP);
+ }
+ }
+
+ // from https://stackoverflow.com/questions/1601151/how-do-i-check-in-sqlite-whether-a-table-exists
+ private static boolean tableExists(SQLiteDatabase db, String tableName) {
+ if (tableName == null || db == null || !db.isOpen()) {
+ return false;
+ }
+ Cursor cursor = db.rawQuery(
+ "SELECT COUNT(*) FROM sqlite_master WHERE type = ? AND name = ?",
+ new String[]{"table", tableName}
+ );
+ if (!cursor.moveToFirst()) {
+ cursor.close();
+ return false;
+ }
+ int count = cursor.getInt(0);
+ cursor.close();
+ return count > 0;
+ }
+
+ private static void execSql(SQLiteDatabase db, String dbgContext, String... ddlStatements) {
+
+ for (String sql : ddlStatements) {
+ if (Global.debugEnabledSql) {
+ Log.i(LOG_TAG, "DatabaseHelper-" + dbgContext + sql);
+ }
db.execSQL(sql);
}
}
@@ -79,9 +128,9 @@ public static void version2Upgrade_RecreateMediDbCopy(final SQLiteDatabase db) {
*/
@Override
public void onCreate(final SQLiteDatabase db) {
- db.execSQL(TransactionLogSql.CREATE_TABLE);
+ execSql(db, "First Create DB: ", TransactionLogSql.CREATE_TABLE);
- this.version2Upgrade_RecreateMediDbCopy(db);
+ version2Upgrade_ReCreateMediaDbTable(db);
}
@Override
@@ -90,7 +139,7 @@ public void onUpgrade(final SQLiteDatabase db, final int oldVersion,
Log.w(this.getClass().toString(), "Upgrading database from version "
+ oldVersion + " to " + newVersion + ". (Old data is kept.)");
if (oldVersion < DatabaseHelper.DATABASE_VERSION_2_MEDIA_DB_COPY) {
- this.version2Upgrade_RecreateMediDbCopy(db);
+ version2Upgrade_ReCreateMediaDbTable(db);
}
}
}
diff --git a/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaContent2DBUpdateService.java b/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaContent2DBUpdateService.java
index ee157c1e..4bc01475 100644
--- a/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaContent2DBUpdateService.java
+++ b/app/src/main/java/de/k3b/android/androFotoFinder/queries/MediaContent2DBUpdateService.java
@@ -55,13 +55,22 @@ public MediaContent2DBUpdateService(Context context, SQLiteDatabase writableData
}
public void clearMediaCopy() {
- DatabaseHelper.version2Upgrade_RecreateMediDbCopy(writableDatabase);
+ DatabaseHelper.version2Upgrade_ReCreateMediaDbTable(writableDatabase);
}
public int rebuild(Context context, IProgessListener progessListener) {
long start = new Date().getTime();
+ if (progessListener != null)
+ progessListener.onProgress(0, 0, "Create Backup of tags, lat, lon, rating");
+ createBackup();
+ if (progessListener != null) progessListener.onProgress(0, 0, "Recreate Database");
clearMediaCopy();
+ if (progessListener != null)
+ progessListener.onProgress(0, 0, "Copy from Android Media Database");
int changeCount = MediaDBRepository.Impl.updateMediaCopy(context, writableDatabase, null, null, progessListener);
+ if (progessListener != null)
+ progessListener.onProgress(0, 0, "Restore tags, lat, lon, rating from Backup");
+ DatabaseHelper.restoreFromBackup(writableDatabase);
long timeInSecs = (new Date().getTime() - start) / 1000;
final String text = "load db " + timeInSecs + " secs";
Toast.makeText(context, text, Toast.LENGTH_LONG).show();
@@ -69,6 +78,10 @@ public int rebuild(Context context, IProgessListener progessListener) {
return changeCount;
}
+ public void createBackup() {
+ DatabaseHelper.createBackup(writableDatabase);
+ }
+
public int update(Context context, IProgessListener progessListener) {
long start = new Date().getTime();
int changeCount = MediaDBRepository.Impl.updateMediaCopy(context, writableDatabase, progessListener);
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 3845d958..3a2e185e 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
@@ -412,13 +412,14 @@ public static ContentValues getContentValues(String fullFilePathFilter, ContentV
public static class Impl {
public static final String DATABASE_TABLE_NAME = "files";
+ public static final String DATABASE_TABLE_NAME_BACKUP = "backup";
/**
* SQL to create copy of contentprovider MediaStore.Images.
* copied from android-4.4 android database. Removed columns not used
*/
protected static final String[] DDL = new String[]{
- "DROP TABLE IF EXISTS \"files\"",
- "CREATE TABLE \"files\" (\n" +
+ "DROP TABLE IF EXISTS \"" + DATABASE_TABLE_NAME + "\"",
+ "CREATE TABLE \"" + DATABASE_TABLE_NAME + "\" (\n" +
"\t_id INTEGER PRIMARY KEY AUTOINCREMENT,\n" +
"\t_size INTEGER,\n" +
"\tdate_added INTEGER,\n" +
@@ -441,11 +442,37 @@ public static class Impl {
"\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 media_type_index ON " + DATABASE_TABLE_NAME + "(media_type)",
+ "CREATE INDEX path_index ON " + DATABASE_TABLE_NAME + "(_data)",
+ "CREATE INDEX sort_index ON " + DATABASE_TABLE_NAME + "(datetaken ASC, _id ASC)",
+ "CREATE INDEX title_idx ON " + DATABASE_TABLE_NAME + "(title)",
};
+ protected static final String[] RESTORE_FROM_BACKUP = new String[]{
+ "UPDATE " + DATABASE_TABLE_NAME + "\n" +
+ "SET latitude = (SELECT latitude FROM " + DATABASE_TABLE_NAME_BACKUP + " WHERE _data = " + DATABASE_TABLE_NAME + "._data)\n" +
+ ", latitude = (SELECT latitude FROM " + DATABASE_TABLE_NAME_BACKUP + " WHERE _data = " + DATABASE_TABLE_NAME + "._data)\n" +
+ ", longitude = (SELECT longitude FROM " + DATABASE_TABLE_NAME_BACKUP + " WHERE _data = " + DATABASE_TABLE_NAME + "._data)\n" +
+ ", tags = (SELECT tags FROM " + DATABASE_TABLE_NAME_BACKUP + " WHERE _data = " + DATABASE_TABLE_NAME + "._data)\n" +
+ ", duration = (SELECT duration FROM " + DATABASE_TABLE_NAME_BACKUP + " WHERE _data = " + DATABASE_TABLE_NAME + "._data)\n" +
+ ", title = (SELECT title FROM " + DATABASE_TABLE_NAME_BACKUP + " WHERE _data = " + DATABASE_TABLE_NAME + "._data)\n" +
+ ", description = (SELECT description FROM " + DATABASE_TABLE_NAME_BACKUP + " WHERE _data = " + DATABASE_TABLE_NAME + "._data)\n" +
+ ", bookmark = (SELECT bookmark FROM " + DATABASE_TABLE_NAME_BACKUP + " WHERE _data = " + DATABASE_TABLE_NAME + "._data)",
+ };
+ private static final String HAS_DATA = "latitude is not null or tags is not null or bookmark is not null";
+ private static final String MY_COLUMNS = " _id, _data, latitude, longitude, tags, duration, bookmark, title, description ";
+ private static final String SELECT_FROM_FILES = " SELECT" + MY_COLUMNS + " from " + DATABASE_TABLE_NAME + "" + " where " + HAS_DATA;
+ protected static final String[] CREATE_BACKUP = new String[]{
+ "DROP TABLE IF EXISTS \"" + DATABASE_TABLE_NAME_BACKUP + "\"",
+ "CREATE TABLE \"" + DATABASE_TABLE_NAME_BACKUP + "\" AS" + SELECT_FROM_FILES,
+ "CREATE INDEX bu_path_index ON " + DATABASE_TABLE_NAME_BACKUP + "(_data)",
+ };
+ protected static final String[] UPDATE_BACKUP = new String[]{
+ "DELETE FROM " + DATABASE_TABLE_NAME_BACKUP + " where exists " +
+ "(select _data from " + DATABASE_TABLE_NAME +
+ " WHERE _data = " + DATABASE_TABLE_NAME_BACKUP + "._data" + " AND (" + HAS_DATA + "))",
+ "INSERT INTO " + DATABASE_TABLE_NAME_BACKUP + "(" + MY_COLUMNS + ")" + SELECT_FROM_FILES,
+ };
+
private static final int COL_INT_MIN = 0;
// same colum order as in DDL
private static final String[] USED_MEDIA_COLUMNS = new String[]{
diff --git a/app/src/main/res/menu/menu_gallery_ao10.xml b/app/src/main/res/menu/menu_gallery_ao10.xml
index 55dc9efe..7c5c0e76 100644
--- a/app/src/main/res/menu/menu_gallery_ao10.xml
+++ b/app/src/main/res/menu/menu_gallery_ao10.xml
@@ -50,6 +50,13 @@
android:title="@string/update_db_menu_title"
android:visible="true" />
+