From ad3364671d48ea697f40dcb31ca5d72232716656 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Tue, 22 Sep 2020 15:45:40 +0200 Subject: [PATCH] Add migration concept for shared preferences --- .../org/schabi/newpipe/report/UserAction.java | 3 +- .../newpipe/settings/NewPipeSettings.java | 19 ++++ .../newpipe/settings/SettingMigrations.java | 106 ++++++++++++++++++ app/src/main/res/values/settings_keys.xml | 4 + 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java diff --git a/app/src/main/java/org/schabi/newpipe/report/UserAction.java b/app/src/main/java/org/schabi/newpipe/report/UserAction.java index faa5e7a44..6fa697f71 100644 --- a/app/src/main/java/org/schabi/newpipe/report/UserAction.java +++ b/app/src/main/java/org/schabi/newpipe/report/UserAction.java @@ -20,7 +20,8 @@ public enum UserAction { DELETE_FROM_HISTORY("delete from history"), PLAY_STREAM("Play stream"), DOWNLOAD_POSTPROCESSING("download post-processing"), - DOWNLOAD_FAILED("download failed"); + DOWNLOAD_FAILED("download failed"), + PREFERENCES_MIGRATION("migration of preferences"); private final String message; diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java index 8ce5fe4c2..aaf2077d2 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -10,6 +10,7 @@ import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; import java.io.File; +import java.util.Set; /* * Created by k3b on 07.01.2016. @@ -38,6 +39,22 @@ public final class NewPipeSettings { private NewPipeSettings() { } public static void initSettings(final Context context) { + // check if there are entries in the prefs to determine whether this is the first app run + Boolean isFirstRun = null; + final Set prefsKeys = PreferenceManager.getDefaultSharedPreferences(context) + .getAll().keySet(); + for (final String key: prefsKeys) { + // ACRA stores some info in the prefs during app initialization + // which happens before this method is called. Therefore ignore ACRA-related keys. + if (!key.toLowerCase().startsWith("acra")) { + isFirstRun = false; + break; + } + } + if (isFirstRun == null) { + isFirstRun = true; + } + PreferenceManager.setDefaultValues(context, R.xml.appearance_settings, true); PreferenceManager.setDefaultValues(context, R.xml.content_settings, true); PreferenceManager.setDefaultValues(context, R.xml.download_settings, true); @@ -48,6 +65,8 @@ public final class NewPipeSettings { getVideoDownloadFolder(context); getAudioDownloadFolder(context); + + SettingMigrations.initMigrations(context, isFirstRun); } private static void getVideoDownloadFolder(final Context context) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java new file mode 100644 index 000000000..5ef92fc25 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -0,0 +1,106 @@ +package org.schabi.newpipe.settings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import androidx.preference.PreferenceManager; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.report.ErrorActivity.ErrorInfo; +import org.schabi.newpipe.report.UserAction; + +import static org.schabi.newpipe.MainActivity.DEBUG; + +public final class SettingMigrations { + private static final String TAG = SettingMigrations.class.toString(); + /** + * Version number for preferences. Must be incremented every time a migration is necessary. + */ + public static final int VERSION = 0; + private static SharedPreferences sp; + + /** + * List of all implemented migrations. + *

+ * Append new migrations to the end of the list to keep it sorted ascending. + * If not sorted correctly, migrations which depend on each other, may fail. + */ + private static final Migration[] SETTING_MIGRATIONS = { + + }; + + + public static void initMigrations(final Context context, final boolean isFirstRun) { + // setup migrations and check if there is something to do + sp = PreferenceManager.getDefaultSharedPreferences(context); + final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version); + final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0); + + // no migration to run, already up to date + if (isFirstRun) { + sp.edit().putInt(lastPrefVersionKey, VERSION).apply(); + return; + } else if (lastPrefVersion == VERSION) { + return; + } + + // run migrations + int currentVersion = lastPrefVersion; + for (final Migration currentMigration : SETTING_MIGRATIONS) { + try { + if (currentMigration.shouldMigrate(currentVersion)) { + if (DEBUG) { + Log.d(TAG, "Migrating preferences from version " + + currentVersion + " to " + currentMigration.newVersion); + } + currentMigration.migrate(context); + currentVersion = currentMigration.newVersion; + } + } catch (final Exception e) { + // save the version with the last successful migration and report the error + sp.edit().putInt(lastPrefVersionKey, currentVersion).apply(); + final ErrorInfo errorInfo = ErrorInfo.make( + UserAction.PREFERENCES_MIGRATION, + "none", + "Migrating preferences from version " + lastPrefVersion + " to " + + VERSION + ". " + + "Error at " + currentVersion + " => " + ++currentVersion, + 0 + ); + ErrorActivity.reportError(context, e, SettingMigrations.class, null, errorInfo); + return; + } + } + + // store the current preferences version + sp.edit().putInt(lastPrefVersionKey, currentVersion).apply(); + } + + private SettingMigrations() { } + + abstract static class Migration { + public final int oldVersion; + public final int newVersion; + + protected Migration(final int oldVersion, final int newVersion) { + this.oldVersion = oldVersion; + this.newVersion = newVersion; + } + + /** + * @param currentVersion current settings version + * @return Returns whether this migration should be run. + * A migration is necessary if the old version of this migration is lower than or equal to + * the current settings version. + */ + private boolean shouldMigrate(final int currentVersion) { + return oldVersion >= currentVersion; + } + + protected abstract void migrate(Context context); + + } + +} diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 88371f6c4..39f48a951 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -1,5 +1,9 @@ + + last_used_version + last_used_preferences_version + @string/youtube