From ad3364671d48ea697f40dcb31ca5d72232716656 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Tue, 22 Sep 2020 15:45:40 +0200 Subject: [PATCH 1/3] 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 From 0e5f85db95b9aed6ad824db591a354633b67319d Mon Sep 17 00:00:00 2001 From: TobiGr Date: Wed, 9 Sep 2020 20:45:42 +0200 Subject: [PATCH 2/3] Remove "Detail Page" open action from share dialog under certain circumstances With the new application workflow and unified player, video detail page and video player are the same activity. So show only one of these options based on whether autoplay is enabled or not, and show both if using external player --- .../org/schabi/newpipe/RouterActivity.java | 59 ++++++++++++++----- .../fragments/detail/VideoDetailFragment.java | 20 +------ .../newpipe/player/helper/PlayerHelper.java | 13 ++++ .../newpipe/settings/SettingMigrations.java | 18 +++++- 4 files changed, 75 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 12fdf8c78..03f232c13 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -39,6 +39,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; @@ -278,6 +279,7 @@ public class RouterActivity extends AppCompatActivity { handleChoice(choice.key); + // open future streams always like this one, because "always" button was used by user if (which == DialogInterface.BUTTON_POSITIVE) { preferences.edit() .putString(getString(R.string.preferred_open_action_key), choice.key) @@ -377,23 +379,50 @@ public class RouterActivity extends AppCompatActivity { final boolean isExtAudioEnabled = preferences.getBoolean( getString(R.string.use_external_audio_player_key), false); - returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key), - getString(R.string.show_info), - resolveResourceIdFromAttr(context, R.attr.ic_info_outline))); + final AdapterChoiceItem videoPlayer = new AdapterChoiceItem( + getString(R.string.video_player_key), getString(R.string.video_player), + resolveResourceIdFromAttr(context, R.attr.ic_play_arrow)); + final AdapterChoiceItem showInfo = new AdapterChoiceItem( + getString(R.string.show_info_key), getString(R.string.show_info), + resolveResourceIdFromAttr(context, R.attr.ic_info_outline)); + final AdapterChoiceItem popupPlayer = new AdapterChoiceItem( + getString(R.string.popup_player_key), getString(R.string.popup_player), + resolveResourceIdFromAttr(context, R.attr.ic_popup)); + final AdapterChoiceItem backgroundPlayer = new AdapterChoiceItem( + getString(R.string.background_player_key), getString(R.string.background_player), + resolveResourceIdFromAttr(context, R.attr.ic_headset)); - if (capabilities.contains(VIDEO) && !(isExtVideoEnabled && linkType != LinkType.STREAM)) { - returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key), - getString(R.string.video_player), - resolveResourceIdFromAttr(context, R.attr.ic_play_arrow))); - returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key), - getString(R.string.popup_player), - resolveResourceIdFromAttr(context, R.attr.ic_popup))); - } + if (linkType == LinkType.STREAM) { + if (isExtVideoEnabled) { + // show both "show info" and "video player", they are two different activities + returnList.add(showInfo); + returnList.add(videoPlayer); + } else if (capabilities.contains(VIDEO) + && PlayerHelper.isAutoplayAllowedByUser(context)) { + // show only "video player" since the details activity will be opened and the video + // will be autoplayed there and "show info" would do the exact same thing + returnList.add(videoPlayer); + } else { + // show only "show info" if video player is not applicable or autoplay is disabled + returnList.add(showInfo); + } - if (capabilities.contains(AUDIO) && !(isExtAudioEnabled && linkType != LinkType.STREAM)) { - returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key), - getString(R.string.background_player), - resolveResourceIdFromAttr(context, R.attr.ic_headset))); + if (capabilities.contains(VIDEO)) { + returnList.add(popupPlayer); + } + if (capabilities.contains(AUDIO)) { + returnList.add(backgroundPlayer); + } + + } else { + returnList.add(showInfo); + if (capabilities.contains(VIDEO) && !isExtVideoEnabled) { + returnList.add(videoPlayer); + returnList.add(popupPlayer); + } + if (capabilities.contains(AUDIO) && !isExtAudioEnabled) { + returnList.add(backgroundPlayer); + } } returnList.add(new AdapterChoiceItem(getString(R.string.download_key), diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 506e8f815..eb889cb00 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -1236,7 +1236,7 @@ public class VideoDetailFragment } private boolean isExternalPlayerEnabled() { - return PreferenceManager.getDefaultSharedPreferences(getContext()) + return PreferenceManager.getDefaultSharedPreferences(requireContext()) .getBoolean(getString(R.string.use_external_video_player_key), false); } @@ -1247,23 +1247,7 @@ public class VideoDetailFragment && !isExternalPlayerEnabled() && (player == null || player.videoPlayerSelected()) && bottomSheetState != BottomSheetBehavior.STATE_HIDDEN - && isAutoplayAllowedByUser(); - } - - private boolean isAutoplayAllowedByUser() { - if (activity == null) { - return false; - } - - switch (PlayerHelper.getAutoplayType(activity)) { - case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER: - return false; - case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI: - return !ListHelper.isMeteredNetwork(activity); - case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS: - default: - return true; - } + && PlayerHelper.isAutoplayAllowedByUser(requireContext()); } private void addVideoPlayerView() { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index 6667d0ecb..fd59e1d99 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -28,6 +28,7 @@ import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; +import org.schabi.newpipe.util.ListHelper; import java.lang.annotation.Retention; import java.text.DecimalFormat; @@ -248,6 +249,18 @@ public final class PlayerHelper { } } + public static boolean isAutoplayAllowedByUser(@NonNull final Context context) { + switch (PlayerHelper.getAutoplayType(context)) { + case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER: + return false; + case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI: + return !ListHelper.isMeteredNetwork(context); + case PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS: + default: + return true; + } + } + @NonNull public static SeekParameters getSeekParameters(@NonNull final Context context) { return isUsingInexactSeek(context) ? SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT; diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java index 5ef92fc25..f2896f078 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -18,9 +18,23 @@ public final class SettingMigrations { /** * Version number for preferences. Must be incremented every time a migration is necessary. */ - public static final int VERSION = 0; + public static final int VERSION = 1; private static SharedPreferences sp; + public static final Migration MIGRATION_0_1 = new Migration(0, 1) { + @Override + public void migrate(final Context context) { + // We changed the content of the dialog which opens when sharing a link to NewPipe + // by removing the "open detail page" option. + // Therefore, show the dialog once again to ensure users need to choose again and are + // aware of the changed dialog. + final SharedPreferences.Editor editor = sp.edit(); + editor.putString(context.getString(R.string.preferred_open_action_key), + context.getString(R.string.always_ask_open_action_key)); + editor.apply(); + } + }; + /** * List of all implemented migrations. *

@@ -28,7 +42,7 @@ public final class SettingMigrations { * If not sorted correctly, migrations which depend on each other, may fail. */ private static final Migration[] SETTING_MIGRATIONS = { - + MIGRATION_0_1 }; From 3c4a4e53840bf5bfe91d1ec1492ab22ab7ddf29a Mon Sep 17 00:00:00 2001 From: TobiGr Date: Wed, 9 Sep 2020 21:08:17 +0200 Subject: [PATCH 3/3] Set default value for "minimize_on_exit" to background for better UX. --- .../newpipe/settings/SettingMigrations.java | 24 +++++++++++++++++-- app/src/main/res/values/settings_keys.xml | 2 +- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java index f2896f078..26e72722e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -18,7 +18,7 @@ public final class SettingMigrations { /** * Version number for preferences. Must be incremented every time a migration is necessary. */ - public static final int VERSION = 1; + public static final int VERSION = 2; private static SharedPreferences sp; public static final Migration MIGRATION_0_1 = new Migration(0, 1) { @@ -35,6 +35,25 @@ public final class SettingMigrations { } }; + public static final Migration MIGRATION_1_2 = new Migration(1, 2) { + @Override + protected void migrate(final Context context) { + // The new application workflow introduced in #2907 allows minimizing videos + // while playing to do other stuff within the app. + // For an even better workflow, we minimize a stream when switching the app to play in + // background. + // Therefore, set default value to background, if it has not been changed yet. + final String minimizeOnExitKey = context.getString(R.string.minimize_on_exit_key); + if (sp.getString(minimizeOnExitKey, "") + .equals(context.getString(R.string.minimize_on_exit_none_key))) { + final SharedPreferences.Editor editor = sp.edit(); + editor.putString(minimizeOnExitKey, + context.getString(R.string.minimize_on_exit_background_key)); + editor.apply(); + } + } + }; + /** * List of all implemented migrations. *

@@ -42,7 +61,8 @@ public final class SettingMigrations { * If not sorted correctly, migrations which depend on each other, may fail. */ private static final Migration[] SETTING_MIGRATIONS = { - MIGRATION_0_1 + MIGRATION_0_1, + MIGRATION_1_2 }; diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 39f48a951..bddad9dae 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -56,7 +56,7 @@ minimize_on_exit_key - @string/minimize_on_exit_none_key + @string/minimize_on_exit_background_key minimize_on_exit_none_key minimize_on_exit_background_key minimize_on_exit_popup_key