diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 1bda3ff7a..251affaed 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/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..26e72722e --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -0,0 +1,140 @@ +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 = 2; + 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(); + } + }; + + 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. + *

+ * 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 = { + MIGRATION_0_1, + MIGRATION_1_2 + }; + + + 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 27829c8a9..878a0c4a7 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 @@ -52,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