mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 07:13:00 +00:00 
			
		
		
		
	Merge pull request #4259 from TeamNewPipe/pref_migration
Add settings migration, remove "Detail page" option from share dialog and minimize to background by default
This commit is contained in:
		| @@ -39,6 +39,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; | |||||||
| import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | 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.ChannelPlayQueue; | ||||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; | import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; | ||||||
| @@ -278,6 +279,7 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|  |  | ||||||
|             handleChoice(choice.key); |             handleChoice(choice.key); | ||||||
|  |  | ||||||
|  |             // open future streams always like this one, because "always" button was used by user | ||||||
|             if (which == DialogInterface.BUTTON_POSITIVE) { |             if (which == DialogInterface.BUTTON_POSITIVE) { | ||||||
|                 preferences.edit() |                 preferences.edit() | ||||||
|                         .putString(getString(R.string.preferred_open_action_key), choice.key) |                         .putString(getString(R.string.preferred_open_action_key), choice.key) | ||||||
| @@ -377,23 +379,50 @@ public class RouterActivity extends AppCompatActivity { | |||||||
|         final boolean isExtAudioEnabled = preferences.getBoolean( |         final boolean isExtAudioEnabled = preferences.getBoolean( | ||||||
|                 getString(R.string.use_external_audio_player_key), false); |                 getString(R.string.use_external_audio_player_key), false); | ||||||
|  |  | ||||||
|         returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key), |         final AdapterChoiceItem videoPlayer = new AdapterChoiceItem( | ||||||
|                 getString(R.string.show_info), |                 getString(R.string.video_player_key), getString(R.string.video_player), | ||||||
|                 resolveResourceIdFromAttr(context, R.attr.ic_info_outline))); |                 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)) { |         if (linkType == LinkType.STREAM) { | ||||||
|             returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key), |             if (isExtVideoEnabled) { | ||||||
|                     getString(R.string.video_player), |                 // show both "show info" and "video player", they are two different activities | ||||||
|                     resolveResourceIdFromAttr(context, R.attr.ic_play_arrow))); |                 returnList.add(showInfo); | ||||||
|             returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key), |                 returnList.add(videoPlayer); | ||||||
|                     getString(R.string.popup_player), |             } else if (capabilities.contains(VIDEO) | ||||||
|                     resolveResourceIdFromAttr(context, R.attr.ic_popup))); |                     && 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)) { |             if (capabilities.contains(VIDEO)) { | ||||||
|             returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key), |                 returnList.add(popupPlayer); | ||||||
|                     getString(R.string.background_player), |             } | ||||||
|                     resolveResourceIdFromAttr(context, R.attr.ic_headset))); |             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), |         returnList.add(new AdapterChoiceItem(getString(R.string.download_key), | ||||||
|   | |||||||
| @@ -1236,7 +1236,7 @@ public class VideoDetailFragment | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private boolean isExternalPlayerEnabled() { |     private boolean isExternalPlayerEnabled() { | ||||||
|         return PreferenceManager.getDefaultSharedPreferences(getContext()) |         return PreferenceManager.getDefaultSharedPreferences(requireContext()) | ||||||
|                 .getBoolean(getString(R.string.use_external_video_player_key), false); |                 .getBoolean(getString(R.string.use_external_video_player_key), false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1247,23 +1247,7 @@ public class VideoDetailFragment | |||||||
|                 && !isExternalPlayerEnabled() |                 && !isExternalPlayerEnabled() | ||||||
|                 && (player == null || player.videoPlayerSelected()) |                 && (player == null || player.videoPlayerSelected()) | ||||||
|                 && bottomSheetState != BottomSheetBehavior.STATE_HIDDEN |                 && bottomSheetState != BottomSheetBehavior.STATE_HIDDEN | ||||||
|                 && isAutoplayAllowedByUser(); |                 && PlayerHelper.isAutoplayAllowedByUser(requireContext()); | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void addVideoPlayerView() { |     private void addVideoPlayerView() { | ||||||
|   | |||||||
| @@ -28,6 +28,7 @@ import org.schabi.newpipe.extractor.stream.VideoStream; | |||||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItem; | import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||||
| import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | import org.schabi.newpipe.player.playqueue.SinglePlayQueue; | ||||||
|  | import org.schabi.newpipe.util.ListHelper; | ||||||
|  |  | ||||||
| import java.lang.annotation.Retention; | import java.lang.annotation.Retention; | ||||||
| import java.text.DecimalFormat; | 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 |     @NonNull | ||||||
|     public static SeekParameters getSeekParameters(@NonNull final Context context) { |     public static SeekParameters getSeekParameters(@NonNull final Context context) { | ||||||
|         return isUsingInexactSeek(context) ? SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT; |         return isUsingInexactSeek(context) ? SeekParameters.CLOSEST_SYNC : SeekParameters.EXACT; | ||||||
|   | |||||||
| @@ -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; | ||||||
|   | |||||||
| @@ -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) { | ||||||
|   | |||||||
| @@ -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. | ||||||
|  |      * <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 = { | ||||||
|  |             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); | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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> | ||||||
| @@ -52,7 +56,7 @@ | |||||||
|     </string-array> |     </string-array> | ||||||
|  |  | ||||||
|     <string name="minimize_on_exit_key" translatable="false">minimize_on_exit_key</string> |     <string name="minimize_on_exit_key" translatable="false">minimize_on_exit_key</string> | ||||||
|     <string name="minimize_on_exit_value" translatable="false">@string/minimize_on_exit_none_key</string> |     <string name="minimize_on_exit_value" translatable="false">@string/minimize_on_exit_background_key</string> | ||||||
|     <string name="minimize_on_exit_none_key" translatable="false">minimize_on_exit_none_key</string> |     <string name="minimize_on_exit_none_key" translatable="false">minimize_on_exit_none_key</string> | ||||||
|     <string name="minimize_on_exit_background_key" translatable="false">minimize_on_exit_background_key</string> |     <string name="minimize_on_exit_background_key" translatable="false">minimize_on_exit_background_key</string> | ||||||
|     <string name="minimize_on_exit_popup_key" translatable="false">minimize_on_exit_popup_key</string> |     <string name="minimize_on_exit_popup_key" translatable="false">minimize_on_exit_popup_key</string> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Tobias Groza
					Tobias Groza