diff --git a/app/build.gradle b/app/build.gradle index bcd29eab..9d4ba150 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,8 +77,8 @@ android { minSdkVersion 26 compileSdkVersion 34 targetSdkVersion 34 - versionCode 31 - versionName "2.0-beta1" + versionCode 34 + versionName "2.0.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -253,7 +253,7 @@ dependencies { // Import the Firebase BoM // When using the BoM, don't specify versions in Firebase dependencies - implementation platform('com.google.firebase:firebase-bom:30.5.0') + implementation platform('com.google.firebase:firebase-bom:32.7.2') // Add the dependency for the Firebase SDKs implementation 'com.google.firebase:firebase-crashlytics' diff --git a/app/schemas/com.grammatek.simaromur.db.ApplicationDb/6.json b/app/schemas/com.grammatek.simaromur.db.ApplicationDb/6.json new file mode 100644 index 00000000..b152c5de --- /dev/null +++ b/app/schemas/com.grammatek.simaromur.db.ApplicationDb/6.json @@ -0,0 +1,201 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "164d16ba8d0a9234b245829c0eba2bc9", + "entities": [ + { + "tableName": "voice_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`voiceId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `gender` TEXT NOT NULL, `internal_name` TEXT NOT NULL, `language_code` TEXT NOT NULL, `language_name` TEXT NOT NULL, `variant` TEXT NOT NULL, `type` TEXT, `update_time` TEXT, `download_time` TEXT, `url` TEXT, `download_path` TEXT, `version` TEXT, `md5_sum` TEXT, `local_size` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "voiceId", + "columnName": "voiceId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "internalName", + "columnName": "internal_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "languageCode", + "columnName": "language_code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "languageName", + "columnName": "language_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "variant", + "columnName": "variant", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updateTime", + "columnName": "update_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadTime", + "columnName": "download_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadPath", + "columnName": "download_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "md5Sum", + "columnName": "md5_sum", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "local_size", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "voiceId" + ] + }, + "indices": [ + { + "name": "index_voice_table_internal_name_gender_language_code_type", + "unique": true, + "columnNames": [ + "internal_name", + "gender", + "language_code", + "type" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_voice_table_internal_name_gender_language_code_type` ON `${TABLE_NAME}` (`internal_name`, `gender`, `language_code`, `type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "app_data_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`appDataId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `schema_version` TEXT, `current_voice_id` INTEGER NOT NULL, `flite_voice_list_path` TEXT, `flite_voice_list_update_time` TEXT, `sim_voice_list_path` TEXT, `sim_voice_list_update_time` TEXT, `privacy_info_dialog_accepted` INTEGER NOT NULL DEFAULT 0, `crash_lytics_user_consent_accepted` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "appDataId", + "columnName": "appDataId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "schemaVersion", + "columnName": "schema_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "currentVoiceId", + "columnName": "current_voice_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fliteVoiceListPath", + "columnName": "flite_voice_list_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fliteVoiceListUpdateTime", + "columnName": "flite_voice_list_update_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "simVoiceListPath", + "columnName": "sim_voice_list_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "simVoiceListUpdateTime", + "columnName": "sim_voice_list_update_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "privacyInfoDialogAccepted", + "columnName": "privacy_info_dialog_accepted", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "crashLyticsUserConsentGiven", + "columnName": "crash_lytics_user_consent_accepted", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "appDataId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '164d16ba8d0a9234b245829c0eba2bc9')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/com.grammatek.simaromur.db.ApplicationDb/7.json b/app/schemas/com.grammatek.simaromur.db.ApplicationDb/7.json new file mode 100644 index 00000000..ef1778b1 --- /dev/null +++ b/app/schemas/com.grammatek.simaromur.db.ApplicationDb/7.json @@ -0,0 +1,201 @@ +{ + "formatVersion": 1, + "database": { + "version": 7, + "identityHash": "164d16ba8d0a9234b245829c0eba2bc9", + "entities": [ + { + "tableName": "voice_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`voiceId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `gender` TEXT NOT NULL, `internal_name` TEXT NOT NULL, `language_code` TEXT NOT NULL, `language_name` TEXT NOT NULL, `variant` TEXT NOT NULL, `type` TEXT, `update_time` TEXT, `download_time` TEXT, `url` TEXT, `download_path` TEXT, `version` TEXT, `md5_sum` TEXT, `local_size` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "voiceId", + "columnName": "voiceId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "internalName", + "columnName": "internal_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "languageCode", + "columnName": "language_code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "languageName", + "columnName": "language_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "variant", + "columnName": "variant", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updateTime", + "columnName": "update_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadTime", + "columnName": "download_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadPath", + "columnName": "download_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "md5Sum", + "columnName": "md5_sum", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "local_size", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "voiceId" + ] + }, + "indices": [ + { + "name": "index_voice_table_internal_name_gender_language_code_type", + "unique": true, + "columnNames": [ + "internal_name", + "gender", + "language_code", + "type" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_voice_table_internal_name_gender_language_code_type` ON `${TABLE_NAME}` (`internal_name`, `gender`, `language_code`, `type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "app_data_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`appDataId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `schema_version` TEXT, `current_voice_id` INTEGER NOT NULL, `flite_voice_list_path` TEXT, `flite_voice_list_update_time` TEXT, `sim_voice_list_path` TEXT, `sim_voice_list_update_time` TEXT, `privacy_info_dialog_accepted` INTEGER NOT NULL DEFAULT 0, `crash_lytics_user_consent_accepted` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "appDataId", + "columnName": "appDataId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "schemaVersion", + "columnName": "schema_version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "currentVoiceId", + "columnName": "current_voice_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "fliteVoiceListPath", + "columnName": "flite_voice_list_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fliteVoiceListUpdateTime", + "columnName": "flite_voice_list_update_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "simVoiceListPath", + "columnName": "sim_voice_list_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "simVoiceListUpdateTime", + "columnName": "sim_voice_list_update_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "privacyInfoDialogAccepted", + "columnName": "privacy_info_dialog_accepted", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "crashLyticsUserConsentGiven", + "columnName": "crash_lytics_user_consent_accepted", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "appDataId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '164d16ba8d0a9234b245829c0eba2bc9')" + ] + } +} diff --git a/app/schemas/com.grammatek.simaromur.db.ApplicationDb/8.json b/app/schemas/com.grammatek.simaromur.db.ApplicationDb/8.json new file mode 100644 index 00000000..6ea4b0c5 --- /dev/null +++ b/app/schemas/com.grammatek.simaromur.db.ApplicationDb/8.json @@ -0,0 +1,183 @@ +{ + "formatVersion": 1, + "database": { + "version": 8, + "identityHash": "2a91f191c1549e1324721eef0e89b0e1", + "entities": [ + { + "tableName": "voice_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`voiceId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `gender` TEXT NOT NULL, `internal_name` TEXT NOT NULL, `language_code` TEXT NOT NULL, `language_name` TEXT NOT NULL, `variant` TEXT NOT NULL, `type` TEXT, `update_time` TEXT, `download_time` TEXT, `url` TEXT, `download_path` TEXT, `version` TEXT, `md5_sum` TEXT, `local_size` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "voiceId", + "columnName": "voiceId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gender", + "columnName": "gender", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "internalName", + "columnName": "internal_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "languageCode", + "columnName": "language_code", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "languageName", + "columnName": "language_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "variant", + "columnName": "variant", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updateTime", + "columnName": "update_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadTime", + "columnName": "download_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "downloadPath", + "columnName": "download_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "md5Sum", + "columnName": "md5_sum", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "local_size", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "voiceId" + ] + }, + "indices": [ + { + "name": "index_voice_table_internal_name_gender_language_code_type", + "unique": true, + "columnNames": [ + "internal_name", + "gender", + "language_code", + "type" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_voice_table_internal_name_gender_language_code_type` ON `${TABLE_NAME}` (`internal_name`, `gender`, `language_code`, `type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "app_data_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`appDataId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `schema_version` TEXT NOT NULL, `current_voice_id` INTEGER NOT NULL, `voice_list_update_time` TEXT, `privacy_info_dialog_accepted` INTEGER NOT NULL DEFAULT 0, `crash_lytics_user_consent_accepted` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "appDataId", + "columnName": "appDataId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "schemaVersion", + "columnName": "schema_version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "currentVoiceId", + "columnName": "current_voice_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "voiceListUpdateTime", + "columnName": "voice_list_update_time", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "privacyInfoDialogAccepted", + "columnName": "privacy_info_dialog_accepted", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "crashLyticsUserConsentGiven", + "columnName": "crash_lytics_user_consent_accepted", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "appDataId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2a91f191c1549e1324721eef0e89b0e1')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/grammatek/simaromur/db/AppData.java b/app/src/main/java/com/grammatek/simaromur/db/AppData.java index 5482f7a8..d412dec2 100644 --- a/app/src/main/java/com/grammatek/simaromur/db/AppData.java +++ b/app/src/main/java/com/grammatek/simaromur/db/AppData.java @@ -15,47 +15,34 @@ public class AppData { // Version string of the database schema @ColumnInfo(name = "schema_version") + @NonNull public String schemaVersion; // Currently chosen voice @ColumnInfo(name = "current_voice_id") public long currentVoiceId; - // - // FLite voices download info - // - - // path to downloaded flite voice list, empty if not yet downloaded - @ColumnInfo(name = "flite_voice_list_path") - public String fliteVoiceListPath; - // last download date/time for non-network FLite voice - @ColumnInfo(name = "flite_voice_list_update_time") + @ColumnInfo(name = "voice_list_update_time") @TypeConverters({TimestampConverter.class}) - public Date fliteVoiceListUpdateTime; - - // - // SIM voices download info - // - - // path to SIM voice list, empty if not yet downloaded - @ColumnInfo(name = "sim_voice_list_path") - public String simVoiceListPath; - - // last download date/time for non-network SIM voice - @ColumnInfo(name = "sim_voice_list_update_time") - @TypeConverters({TimestampConverter.class}) - public Date simVoiceListUpdateTime; + public Date voiceListUpdateTime; // boolean for privacy info dialog acceptance of the user @ColumnInfo(name = "privacy_info_dialog_accepted", defaultValue = "0") @NonNull - public Boolean privacyInfoDialogAccepted = false; + public Boolean privacyInfoDialogAccepted; // boolean for CrashLytics user consent @ColumnInfo(name = "crash_lytics_user_consent_accepted", defaultValue = "0") @NonNull - public Boolean crashLyticsUserConsentGiven = false; + public Boolean crashLyticsUserConsentGiven; + + public AppData() { + schemaVersion = "8"; + currentVoiceId = -1; + privacyInfoDialogAccepted = false; + crashLyticsUserConsentGiven = false; + } @NonNull @Override @@ -64,10 +51,7 @@ public String toString() { "appDataId=" + appDataId + ", schemaVersion='" + schemaVersion + '\'' + ", currentVoiceId=" + currentVoiceId + - ", fliteVoiceListPath='" + fliteVoiceListPath + '\'' + - ", fliteVoiceListUpdateTime=" + fliteVoiceListUpdateTime + - ", simVoiceListPath='" + simVoiceListPath + '\'' + - ", simVoiceListUpdateTime=" + simVoiceListUpdateTime + + ", voiceListUpdateTime=" + voiceListUpdateTime + ", privacyInfoDialogAccepted=" + privacyInfoDialogAccepted + ", crashLyticsUserConsentGiven=" + crashLyticsUserConsentGiven + '}'; diff --git a/app/src/main/java/com/grammatek/simaromur/db/AppDataDao.java b/app/src/main/java/com/grammatek/simaromur/db/AppDataDao.java index f3e06f94..47113133 100644 --- a/app/src/main/java/com/grammatek/simaromur/db/AppDataDao.java +++ b/app/src/main/java/com/grammatek/simaromur/db/AppDataDao.java @@ -62,8 +62,8 @@ public void doGiveCrashLyticsUserConsent(Boolean setter) { * @param voice the current voice to select, it needs to already exist in db */ public void selectCurrentVoice(Voice voice) { - if ((voice.voiceId == 0)) - throw new AssertionError("selectCurrentVoice: voiceId == 0"); + if ((voice.voiceId <= 0)) + throw new AssertionError("selectCurrentVoice: voiceId <= 0"); AppData appData = getAppData(); appData.currentVoiceId = voice.voiceId; update(appData); @@ -72,7 +72,7 @@ public void selectCurrentVoice(Voice voice) { /** * Returns id of current voice from AppData table. * - * @return voice id of the current selected voice + * @return voice id of the current selected voice or -1 if no voice is selected */ public Long getCurrentVoiceId() { AppData appData = getAppData(); @@ -84,8 +84,8 @@ public Long getCurrentVoiceId() { */ public void updateVoiceListTimestamp() { AppData appData = getAppData(); - appData.simVoiceListUpdateTime = new java.util.Date(); - Log.v(LOG_TAG, "updateVoiceListTimestamp: " + appData.simVoiceListUpdateTime); + appData.voiceListUpdateTime = new java.util.Date(); + Log.v(LOG_TAG, "updateVoiceListTimestamp: " + appData.voiceListUpdateTime); update(appData); } @@ -94,10 +94,10 @@ public boolean voiceListUpdateTimeOlderThan(java.util.Date date) { if (appData == null) { return true; } - Date lastUpdate = appData.simVoiceListUpdateTime; + Date lastUpdate = appData.voiceListUpdateTime; if (lastUpdate == null) { return true; } - return (appData.simVoiceListUpdateTime.before(date)); + return (appData.voiceListUpdateTime.before(date)); } } diff --git a/app/src/main/java/com/grammatek/simaromur/db/ApplicationDb.java b/app/src/main/java/com/grammatek/simaromur/db/ApplicationDb.java index eb04a8fc..e1870570 100644 --- a/app/src/main/java/com/grammatek/simaromur/db/ApplicationDb.java +++ b/app/src/main/java/com/grammatek/simaromur/db/ApplicationDb.java @@ -22,7 +22,7 @@ ) public abstract class ApplicationDb extends RoomDatabase { private final static String LOG_TAG = "Simaromur_" + ApplicationDb.class.getSimpleName(); - static final int LATEST_VERSION = 7; + static final int LATEST_VERSION = 8; private static volatile ApplicationDb INSTANCE; public abstract AppDataDao appDataDao(); @@ -78,6 +78,43 @@ public void migrate(SupportSQLiteDatabase database){ database.execSQL("UPDATE voice_table SET url = 'https://api.grammatek.com/tts/v0' WHERE type = 'network'"); } }; + + static public final Migration MIGRATION_7_8 = new Migration(7, 8) { // v7 => 8 + @Override + public void migrate(SupportSQLiteDatabase database) { + Log.v(LOG_TAG, "MIGRATION_7_8"); + + // adapt voice_table to new schema: delete all but onnx voices and update schema version + database.execSQL("DELETE FROM voice_table WHERE type != 'onnx'"); + database.execSQL("UPDATE app_data_table SET schema_version = '8'"); + + // adapt app_data_table to new schema: drop unnecessary columns. This is not directly + // supported in SQLite. We need to create a new table, copy the data over and finally + // drop the old table. Please take into account the definitions of AppData class ! + database.execSQL( + "CREATE TABLE IF NOT EXISTS app_data_table_temp (`appDataId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `schema_version` TEXT NOT NULL, `current_voice_id` INTEGER NOT NULL, `voice_list_update_time` TEXT, `privacy_info_dialog_accepted` INTEGER NOT NULL DEFAULT 0, `crash_lytics_user_consent_accepted` INTEGER NOT NULL DEFAULT 0)"); + database.execSQL( + "INSERT INTO app_data_table_temp SELECT appDataId, schema_version, current_voice_id, sim_voice_list_update_time, privacy_info_dialog_accepted, crash_lytics_user_consent_accepted FROM app_data_table" + ); + database.execSQL("DROP TABLE app_data_table"); + database.execSQL("ALTER TABLE app_data_table_temp RENAME TO app_data_table"); + + // if the current_voice_id does not exist in voice_table or is NULL, set it to the first voice found in voice_table, + // if there is no entry in voice_table, set it to -1 + database.execSQL( + "UPDATE app_data_table " + + "SET current_voice_id = CASE " + + "WHEN current_voice_id IN (SELECT voiceId FROM voice_table) THEN current_voice_id " + + "ELSE -1 " + + "END " + + "WHERE current_voice_id NOT IN (SELECT voiceId FROM voice_table) OR current_voice_id IS NULL;" + ); + + // clean up the database file: doesn't work inside a transaction + //database.execSQL("VACUUM"); + } + }; + public static ApplicationDb getDatabase(final Context context) { Log.v(LOG_TAG, "getDatabase"); if (INSTANCE == null) { @@ -85,7 +122,7 @@ public static ApplicationDb getDatabase(final Context context) { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder(context.getApplicationContext(), ApplicationDb.class, "application_db") - .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7) + .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8) // Wipes and rebuilds instead of migrating if no Migration object. .fallbackToDestructiveMigration() .allowMainThreadQueries() @@ -128,15 +165,19 @@ protected Void doInBackground(final Void... params) { AppData appData = mAppDataDao.getAppData(); if (appData == null) { + // initialize fresh app data table values. This is after a fresh install + Log.i(LOG_TAG, "PopulateDbAsync: Fresh install, initializing App data table"); appData = new AppData(); - appData.fliteVoiceListPath = App.getDataPath(); - appData.simVoiceListPath = App.getVoiceDataPath(); mAppDataDao.insert(appData); + } else { + Log.v(LOG_TAG, "PopulateDbAsync: App data table found."); } List voices = mVoiceDao.getAnyVoices(); if (voices == null || voices.isEmpty()) { - Log.d(LOG_TAG, "PopulateDbAsync: no voices yet, network voices are updated async."); + Log.d(LOG_TAG, "PopulateDbAsync: no voices yet"); + } else { + Log.d(LOG_TAG, "PopulateDbAsync: Voices found."); } return null; } diff --git a/app/src/test/java/com/grammatek/simaromur/TestRoomDbMigration.java b/app/src/test/java/com/grammatek/simaromur/TestRoomDbMigration.java index 9f12b513..d4e10188 100644 --- a/app/src/test/java/com/grammatek/simaromur/TestRoomDbMigration.java +++ b/app/src/test/java/com/grammatek/simaromur/TestRoomDbMigration.java @@ -77,7 +77,7 @@ public void migrationFrom3To4() throws IOException { // Create the database in version 3, then migrate to 4 SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 3); - db.execSQL("INSERT INTO app_data_table VALUES (1, '2', 1, '/flite/voices', 'today'," + + db.execSQL("INSERT INTO app_data_table VALUES (1, '3', 1, '/flite/voices', 'today'," + " '/sim/voices', 'today', 1)"); db.execSQL("INSERT INTO voice_table VALUES (1, 'Álfur', 'male', 'Alfur', 'is-IS', 'Íslenska(icelandic)'," + @@ -91,7 +91,7 @@ public void migrationFrom4To5() throws IOException { // Create the database in version 4, then migrate to 5 SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 4); - db.execSQL("INSERT INTO app_data_table VALUES (1, '2', 1, '/flite/voices', 'today'," + + db.execSQL("INSERT INTO app_data_table VALUES (1, '4', 1, '/flite/voices', 'today'," + " '/sim/voices', 'today', 1, 1)"); db.execSQL("INSERT INTO voice_table VALUES (1, 'Álfur', 'male', 'Alfur', 'is-IS', 'Íslenska(icelandic)'," + @@ -105,7 +105,7 @@ public void migrationFrom5To6() throws IOException { // Create the database in version 5, then migrate to 6 SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 5); - db.execSQL("INSERT INTO app_data_table VALUES (1, '2', 1, '/flite/voices', 'today'," + + db.execSQL("INSERT INTO app_data_table VALUES (1, '5', 1, '/flite/voices', 'today'," + " '/sim/voices', 'today', 1, 1)"); db.execSQL("INSERT INTO voice_table VALUES (1, 'Álfur', 'male', 'Alfur', 'is-IS', 'Íslenska(icelandic)'," + @@ -119,7 +119,7 @@ public void migrationFrom6To7() throws IOException { // Create the database in version 6, then migrate to 7 SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 6); - db.execSQL("INSERT INTO app_data_table VALUES (1, '2', 1, '/flite/voices', 'today'," + + db.execSQL("INSERT INTO app_data_table VALUES (1, '6', 1, '/flite/voices', 'today'," + " '/sim/voices', 'today', 1, 1)"); db.execSQL("INSERT INTO voice_table VALUES (1, 'Álfur', 'male', 'Alfur', 'is-IS', 'Íslenska(icelandic)'," + @@ -128,6 +128,33 @@ public void migrationFrom6To7() throws IOException { testHelper.runMigrationsAndValidate(TEST_DB_NAME, 7, true, ApplicationDb.MIGRATION_6_7); } @Test + public void migrationFrom7To8() throws IOException { + // Create the database in version 7, then migrate to 8 + SupportSQLiteDatabase db = + testHelper.createDatabase(TEST_DB_NAME, 7); + db.execSQL("INSERT INTO app_data_table VALUES (1, '7', 2, '/flite/voices', 'today'," + + " '/sim/voices', 'today', 1, 1)"); + + db.execSQL("INSERT INTO voice_table VALUES (1, 'Álfur', 'male', 'Alfur', 'is-IS', 'Íslenska(icelandic)'," + + " '', 'network', 'now', 'now', 'http://someurl', 'downloadpath', 'API1', 'nomd5sum', 0)"); + db.execSQL("INSERT INTO voice_table VALUES (2, 'Steinn', 'male', 'stein_vits_onnx_xs_ipa', 'isl-ISL', 'Íslenska'," + + " 'Steinn', 'onnx', 'now', 'now', 'assets', 'is-steinn-xs-ipa.onnx is-steinn-xs-ipa.onnx.json', '0.5', '31a565683d0927cb8a39d5f80ebd85c1', 0)"); + db.close(); + testHelper.runMigrationsAndValidate(TEST_DB_NAME, 8, true, ApplicationDb.MIGRATION_7_8); + } + @Test + public void migrationFrom7To8NoValidVoicesLeft() throws IOException { + // Create the database in version 7 with only non-ONNX voice, then migrate to 8 + SupportSQLiteDatabase db = + testHelper.createDatabase(TEST_DB_NAME, 7); + db.execSQL("INSERT INTO voice_table VALUES (1, 'Álfur', 'male', 'Alfur', 'is-IS', 'Íslenska(icelandic)'," + + " '', 'network', 'now', 'now', 'http://someurl', 'downloadpath', 'API1', 'nomd5sum', 0)"); + db.execSQL("INSERT INTO app_data_table VALUES (1, '7', 1, '/flite/voices', 'today'," + + " '/sim/voices', 'today', 1, 1)"); + db.close(); + testHelper.runMigrationsAndValidate(TEST_DB_NAME, 8, true, ApplicationDb.MIGRATION_7_8); + } + @Test public void TestDBV2() throws IOException { // Create DB in V2 SupportSQLiteDatabase db = @@ -161,7 +188,7 @@ public void TestDBV3() throws IOException { // Create DB in V3 SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 3); - db.execSQL("INSERT INTO app_data_table VALUES (1, '3', 2, '/flite/voices', 'today'," + + db.execSQL("INSERT INTO app_data_table VALUES (1, '3', 1, '/flite/voices', 'today'," + " '/sim/voices', 'today', 1)"); db.execSQL("INSERT INTO voice_table VALUES (1, 'Álfur', 'male', 'Alfur', 'is-IS', 'Íslenska(icelandic)'," + @@ -175,7 +202,7 @@ public void TestDBV4() throws IOException { // Create DB in V4 SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 4); - db.execSQL("INSERT INTO app_data_table VALUES (1, '3', 2, '/flite/voices', 'today'," + + db.execSQL("INSERT INTO app_data_table VALUES (1, '4', 1, '/flite/voices', 'today'," + " '/sim/voices', 'today', 1, 1)"); db.execSQL("UPDATE app_data_table SET privacy_info_dialog_accepted = 0, crash_lytics_user_consent_accepted = 0 WHERE appDataId = 1"); @@ -191,7 +218,7 @@ public void TestDBV5() throws IOException { // Create DB in V5 SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 5); - db.execSQL("INSERT INTO app_data_table VALUES (1, '3', 2, '/flite/voices', 'today'," + + db.execSQL("INSERT INTO app_data_table VALUES (1, '5',1, '/flite/voices', 'today'," + " '/sim/voices', 'today', 1, 1)"); db.execSQL("UPDATE app_data_table SET privacy_info_dialog_accepted = 0, crash_lytics_user_consent_accepted = 0 WHERE appDataId = 1"); @@ -203,10 +230,10 @@ public void TestDBV5() throws IOException { } @Test public void TestDBV6() throws IOException { - // Create DB in V5 + // Create DB in V6 SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 6); - db.execSQL("INSERT INTO app_data_table VALUES (1, '3', 2, '/flite/voices', 'today'," + + db.execSQL("INSERT INTO app_data_table VALUES (1, '6', 1, '/flite/voices', 'today'," + " '/sim/voices', 'today', 1, 1)"); db.execSQL("UPDATE app_data_table SET privacy_info_dialog_accepted = 0, crash_lytics_user_consent_accepted = 0 WHERE appDataId = 1"); @@ -218,10 +245,10 @@ public void TestDBV6() throws IOException { } @Test public void TestDBV7() throws IOException { - // Create DB in V5 + // Create DB in V7 SupportSQLiteDatabase db = testHelper.createDatabase(TEST_DB_NAME, 7); - db.execSQL("INSERT INTO app_data_table VALUES (1, '3', 2, '/flite/voices', 'today'," + + db.execSQL("INSERT INTO app_data_table VALUES (1, '7', 1, '/flite/voices', 'today'," + " '/sim/voices', 'today', 1, 1)"); db.execSQL("UPDATE app_data_table SET privacy_info_dialog_accepted = 0, crash_lytics_user_consent_accepted = 0 WHERE appDataId = 1"); @@ -229,6 +256,20 @@ public void TestDBV7() throws IOException { db.execSQL("INSERT INTO voice_table VALUES (1, 'Álfur', 'male', 'Alfur', 'is-IS', 'Íslenska(icelandic)'," + " '', 'network', 'now', 'now', 'http://someurl', 'downloadpath', 'API1', 'nomd5sum', 0)"); + db.close(); + } + @Test + public void TestDBV8() throws IOException { + // Create DB in V8 + SupportSQLiteDatabase db = + testHelper.createDatabase(TEST_DB_NAME, 8); + + db.execSQL("INSERT INTO voice_table VALUES (1, 'Steinn', 'male', 'stein_vits_onnx_xs_ipa', 'isl-ISL', 'Íslenska'," + + " 'Steinn', 'onnx', 'now', 'now', 'assets', 'is-steinn-xs-ipa.onnx is-steinn-xs-ipa.onnx.json', '0.5', '31a565683d0927cb8a39d5f80ebd85c1', 0)"); + + db.execSQL("INSERT INTO app_data_table VALUES (1, '8', 1, 'now', 1, 1)"); + db.execSQL("UPDATE app_data_table SET privacy_info_dialog_accepted = 0, crash_lytics_user_consent_accepted = 0 WHERE appDataId = 1"); + db.close(); } }