Add migration concept for shared preferences

This commit is contained in:
TobiGr 2020-09-22 15:45:40 +02:00 committed by Stypox
parent e0f02d4080
commit ad3364671d
No known key found for this signature in database
GPG key ID: 4BDF1B40A49FDD23
4 changed files with 131 additions and 1 deletions

View file

@ -20,7 +20,8 @@ public enum UserAction {
DELETE_FROM_HISTORY("delete from history"), DELETE_FROM_HISTORY("delete from history"),
PLAY_STREAM("Play stream"), PLAY_STREAM("Play stream"),
DOWNLOAD_POSTPROCESSING("download post-processing"), DOWNLOAD_POSTPROCESSING("download post-processing"),
DOWNLOAD_FAILED("download failed"); DOWNLOAD_FAILED("download failed"),
PREFERENCES_MIGRATION("migration of preferences");
private final String message; private final String message;

View file

@ -10,6 +10,7 @@ import androidx.preference.PreferenceManager;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import java.io.File; import java.io.File;
import java.util.Set;
/* /*
* Created by k3b on 07.01.2016. * Created by k3b on 07.01.2016.
@ -38,6 +39,22 @@ public final class NewPipeSettings {
private NewPipeSettings() { } private NewPipeSettings() { }
public static void initSettings(final Context context) { 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<String> 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.appearance_settings, true);
PreferenceManager.setDefaultValues(context, R.xml.content_settings, true); PreferenceManager.setDefaultValues(context, R.xml.content_settings, true);
PreferenceManager.setDefaultValues(context, R.xml.download_settings, true); PreferenceManager.setDefaultValues(context, R.xml.download_settings, true);
@ -48,6 +65,8 @@ public final class NewPipeSettings {
getVideoDownloadFolder(context); getVideoDownloadFolder(context);
getAudioDownloadFolder(context); getAudioDownloadFolder(context);
SettingMigrations.initMigrations(context, isFirstRun);
} }
private static void getVideoDownloadFolder(final Context context) { private static void getVideoDownloadFolder(final Context context) {

View file

@ -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.
* <p>
* <b>Append new migrations to the end of the list</b> 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);
}
}

View file

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources translatable="false"> <resources translatable="false">
<!-- App versioning -->
<string name="last_used_version" translatable="false">last_used_version</string>
<string name="last_used_preferences_version" translatable="false">last_used_preferences_version</string>
<!-- Service --> <!-- Service -->
<string-array name="service_list" translatable="false"> <string-array name="service_list" translatable="false">
<item>@string/youtube</item> <item>@string/youtube</item>