From 14223f40b5ca48f35bbecfd849dff20dfd309d92 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Fri, 14 Apr 2023 05:50:16 +0400 Subject: [PATCH] feat(youtube): user selectable default video speed and quality (#354) Co-authored-by: johnconner122 <107796137+johnconner122@users.noreply.github.com> Co-authored-by: oSumAtrIX --- .../quality/RememberVideoQualityPatch.java | 233 ++++++++++-------- .../playback/speed/CustomVideoSpeedPatch.java | 13 +- .../speed/RememberPlaybackSpeedPatch.java | 42 +++- .../integrations/settings/SettingsEnum.java | 60 ++++- .../settings/SharedPrefCategory.java | 74 ++++-- .../ReVancedSettingsFragment.java | 68 +++-- 6 files changed, 321 insertions(+), 169 deletions(-) diff --git a/app/src/main/java/app/revanced/integrations/patches/playback/quality/RememberVideoQualityPatch.java b/app/src/main/java/app/revanced/integrations/patches/playback/quality/RememberVideoQualityPatch.java index 695b52ac..df85317d 100644 --- a/app/src/main/java/app/revanced/integrations/patches/playback/quality/RememberVideoQualityPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/playback/quality/RememberVideoQualityPatch.java @@ -1,139 +1,168 @@ package app.revanced.integrations.patches.playback.quality; -import android.content.Context; +import static app.revanced.integrations.utils.ReVancedUtils.NetworkType; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Collections; +import java.util.List; import app.revanced.integrations.settings.SettingsEnum; -import app.revanced.integrations.settings.SharedPrefCategory; import app.revanced.integrations.utils.LogHelper; import app.revanced.integrations.utils.ReVancedUtils; -import app.revanced.integrations.utils.ReVancedUtils.NetworkType; public class RememberVideoQualityPatch { - public static int selectedQuality1 = -2; - private static Boolean newVideo = false; - private static Boolean userChangedQuality = false; + private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2; + private static final SettingsEnum wifiQualitySetting = SettingsEnum.VIDEO_QUALITY_DEFAULT_WIFI; + private static final SettingsEnum mobileQualitySetting = SettingsEnum.VIDEO_QUALITY_DEFAULT_MOBILE; - public static void changeDefaultQuality(int defaultQuality) { - Context context = ReVancedUtils.getContext(); + private static boolean qualityNeedsUpdating; + @Nullable + private static String currentVideoId; - var networkType = ReVancedUtils.getNetworkType(); + /** + * If the user selected a new quality from the flyout menu, + * and {@link SettingsEnum#VIDEO_QUALITY_REMEMBER_LAST_SELECTED} is enabled. + */ + private static boolean userChangedDefaultQuality; + /** + * Index of the video quality chosen by the user from the flyout menu. + */ + private static int userSelectedQualityIndex; + + /** + * The available qualities of the current video in human readable form: [1080, 720, 480] + */ + @Nullable + private static List videoQualities; + + private static void changeDefaultQuality(int defaultQuality) { + NetworkType networkType = ReVancedUtils.getNetworkType(); if (networkType == NetworkType.NONE) { - ReVancedUtils.showToastShort("No internet connection."); - } else { - var preferenceKey = "wifi_quality"; - var networkTypeMessage = "WIFI"; - - if (networkType == NetworkType.MOBILE) { - networkTypeMessage = "mobile"; - preferenceKey = "mobile_quality"; - } - - SharedPrefCategory.REVANCED_PREFS.saveString(preferenceKey, String.valueOf(defaultQuality)); - ReVancedUtils.showToastShort("Changing default " + networkTypeMessage + " quality to: " + defaultQuality); + ReVancedUtils.showToastShort("No internet connection"); + return; } - - userChangedQuality = false; + String networkTypeMessage; + if (networkType == NetworkType.MOBILE) { + mobileQualitySetting.saveValue(defaultQuality); + networkTypeMessage = "mobile"; + } else { + wifiQualitySetting.saveValue(defaultQuality); + networkTypeMessage = "Wi-Fi"; + } + ReVancedUtils.showToastShort("Changed default " + networkTypeMessage + + " quality to: " + defaultQuality +"p"); } - public static int setVideoQuality(Object[] qualities, int quality, Object qInterface, String qIndexMethod) { - Field[] fields; - - if (!(newVideo || userChangedQuality) || qInterface == null) { - return quality; - } - - Class intType = Integer.TYPE; - ArrayList iStreamQualities = new ArrayList<>(); + /** + * Injection point. + * + * @param qualities Video qualities available, ordered from largest to smallest, with index 0 being the 'automatic' value of -2 + * @param originalQualityIndex quality index to use, as chosen by YouTube + */ + public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) { try { - for (Object streamQuality : qualities) { - for (Field field : streamQuality.getClass().getFields()) { - if (field.getType().isAssignableFrom(intType)) { // converts quality index to actual readable resolution - int value = field.getInt(streamQuality); - if (field.getName().length() <= 2) { - iStreamQualities.add(value); + if (!(qualityNeedsUpdating || userChangedDefaultQuality) || qInterface == null) { + return originalQualityIndex; + } + qualityNeedsUpdating = false; + + final int preferredQuality; + if (ReVancedUtils.getNetworkType() == NetworkType.MOBILE) { + preferredQuality = mobileQualitySetting.getInt(); + } else { + preferredQuality = wifiQualitySetting.getInt(); + } + if (!userChangedDefaultQuality && preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) { + return originalQualityIndex; // nothing to do + } + + if (videoQualities == null || videoQualities.size() != qualities.length) { + videoQualities = new ArrayList<>(qualities.length); + for (Object streamQuality : qualities) { + for (Field field : streamQuality.getClass().getFields()) { + if (field.getType().isAssignableFrom(Integer.TYPE) + && field.getName().length() <= 2) { + videoQualities.add(field.getInt(streamQuality)); } } } + LogHelper.printDebug(() -> "VideoId: " + currentVideoId + " videoQualities: " + videoQualities); } - } catch (Exception ignored) { - } - Collections.sort(iStreamQualities); - int index = 0; - if (userChangedQuality) { - for (int convertedQuality : iStreamQualities) { - int selectedQuality2 = qualities.length - selectedQuality1 + 1; - index++; - if (selectedQuality2 == index) { - final int indexToLog = index; // must be final for lambda - LogHelper.printDebug(() -> "Quality index is: " + indexToLog + " and corresponding value is: " + convertedQuality); - changeDefaultQuality(convertedQuality); - return selectedQuality2; + + if (userChangedDefaultQuality) { + userChangedDefaultQuality = false; + final int quality = videoQualities.get(userSelectedQualityIndex); + LogHelper.printDebug(() -> "User changed default quality to: " + quality); + changeDefaultQuality(quality); + return userSelectedQualityIndex; + } + + // find the highest quality that is equal to or less than the preferred + int qualityToUse = videoQualities.get(0); // first element is automatic mode + int qualityIndexToUse = 0; + int i = 0; + for (Integer quality : videoQualities) { + if (quality <= preferredQuality && qualityToUse < quality) { + qualityToUse = quality; + qualityIndexToUse = i; } + i++; + } + if (qualityIndexToUse == originalQualityIndex) { + LogHelper.printDebug(() -> "Video is already preferred quality: " + preferredQuality); + return originalQualityIndex; } - } - newVideo = false; - final int qualityToLog = quality; - LogHelper.printDebug(() -> "Quality: " + qualityToLog); - Context context = ReVancedUtils.getContext(); - if (context == null) { - LogHelper.printException(() -> "Context is null or settings not initialized, returning quality: " + qualityToLog); - return quality; - } - var networkType = ReVancedUtils.getNetworkType(); - if (networkType == NetworkType.NONE) { - LogHelper.printDebug(() -> "No Internet connection!"); - return quality; - } else { - var preferenceKey = "wifi_quality"; - if (networkType == NetworkType.MOBILE) preferenceKey = "mobile_quality"; - int preferredQuality = SharedPrefCategory.REVANCED_PREFS.getInt(preferenceKey, -2); - if (preferredQuality == -2) return quality; + final int qualityToUseLog = qualityToUse; + LogHelper.printDebug(() -> "Quality changed from: " + + videoQualities.get(originalQualityIndex) + " to: " + qualityToUseLog); - for (int streamQuality2 : iStreamQualities) { - final int indexToLog = index; - LogHelper.printDebug(() -> "Quality at index " + indexToLog + ": " + streamQuality2); - index++; - } - for (Integer iStreamQuality : iStreamQualities) { - int streamQuality3 = iStreamQuality; - if (streamQuality3 <= preferredQuality) { - quality = streamQuality3; - } - } - if (quality == -2) return quality; - - int qualityIndex = iStreamQualities.indexOf(quality); - final int qualityToLog2 = quality; - LogHelper.printDebug(() -> "Index of quality " + qualityToLog2 + " is " + qualityIndex); - try { - Class cl = qInterface.getClass(); - Method m = cl.getMethod(qIndexMethod, Integer.TYPE); - LogHelper.printDebug(() -> "Method is: " + qIndexMethod); - m.invoke(qInterface, iStreamQualities.get(qualityIndex)); - LogHelper.printDebug(() -> "Quality changed to: " + qualityIndex); - return qualityIndex; - } catch (Exception ex) { - LogHelper.printException(() -> "Failed to set quality", ex); - return qualityIndex; - } + Method m = qInterface.getClass().getMethod(qIndexMethod, Integer.TYPE); + m.invoke(qInterface, qualityToUse); + return qualityIndexToUse; + } catch (Exception ex) { + LogHelper.printException(() -> "Failed to set quality", ex); + return originalQualityIndex; } } + /** + * Injection point. + */ public static void userChangedQuality(int selectedQuality) { - if (!SettingsEnum.REMEMBER_VIDEO_QUALITY_LAST_SELECTED.getBoolean()) return; + if (!SettingsEnum.VIDEO_QUALITY_REMEMBER_LAST_SELECTED.getBoolean()) return; - selectedQuality1 = selectedQuality; - userChangedQuality = true; + userSelectedQualityIndex = selectedQuality; + userChangedDefaultQuality = true; } - public static void newVideoStarted(String videoId) { - newVideo = true; + /** + * Injection point. + */ + public static void newVideoStarted(@NonNull String videoId) { + // The same videoId can be passed in multiple times for a single video playback. + // Such as closing and opening the app, and sometimes when turning off/on the device screen. + // + // Known limitation, if: + // 1. a default video quality exists, and remember quality is turned off + // 2. user opens a video + // 3. user changes the video quality + // 4. user turns off then on the device screen (or does anything else that triggers the video id hook) + // result: the video quality of the current video will revert back to the saved default + // + // qualityNeedsUpdating could be set only when the videoId changes + // but then if the user closes and re-opens the same video the default video quality will not be applied. + LogHelper.printDebug(() -> "newVideoStarted: " + videoId); + qualityNeedsUpdating = true; + + if (!videoId.equals(currentVideoId)) { + currentVideoId = videoId; + videoQualities = null; + } } } diff --git a/app/src/main/java/app/revanced/integrations/patches/playback/speed/CustomVideoSpeedPatch.java b/app/src/main/java/app/revanced/integrations/patches/playback/speed/CustomVideoSpeedPatch.java index 663fe6b1..6692dc85 100644 --- a/app/src/main/java/app/revanced/integrations/patches/playback/speed/CustomVideoSpeedPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/playback/speed/CustomVideoSpeedPatch.java @@ -1,9 +1,12 @@ package app.revanced.integrations.patches.playback.speed; public class CustomVideoSpeedPatch { - // Values are useless as they are being overridden by the respective patch. - // This generates a .array segment in Dalvik bytecode - // which the patch utilizes to store the video speeds in, only - // if it has two or more default values. - public static final float[] videoSpeeds = { 0, 0 }; + /** + * Default playback speeds offered by YouTube. + * Values are also used by {@link RememberPlaybackSpeedPatch}. + * + * If custom video speed is applied, + * then this array is overwritten by the patch with custom speeds + */ + public static final float[] videoSpeeds = {0.25f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f}; } diff --git a/app/src/main/java/app/revanced/integrations/patches/playback/speed/RememberPlaybackSpeedPatch.java b/app/src/main/java/app/revanced/integrations/patches/playback/speed/RememberPlaybackSpeedPatch.java index 5b4a9460..b3475944 100644 --- a/app/src/main/java/app/revanced/integrations/patches/playback/speed/RememberPlaybackSpeedPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/playback/speed/RememberPlaybackSpeedPatch.java @@ -1,5 +1,7 @@ package app.revanced.integrations.patches.playback.speed; +import android.preference.ListPreference; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -9,6 +11,11 @@ import app.revanced.integrations.utils.ReVancedUtils; public final class RememberPlaybackSpeedPatch { + /** + * PreferenceList entries and values, of all available playback speeds. + */ + private static String[] preferenceListEntries, preferenceListEntryValues; + @Nullable private static String currentVideoId; @@ -21,7 +28,7 @@ public final class RememberPlaybackSpeedPatch { return; } currentVideoId = videoId; - VideoInformation.overridePlaybackSpeed(SettingsEnum.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED_VALUE.getFloat()); + VideoInformation.overridePlaybackSpeed(SettingsEnum.PLAYBACK_SPEED_DEFAULT.getFloat()); } /** @@ -31,13 +38,9 @@ public final class RememberPlaybackSpeedPatch { * @param playbackSpeed The playback speed the user selected */ public static void userSelectedPlaybackSpeed(float playbackSpeed) { - if (SettingsEnum.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED.getBoolean()) { - SettingsEnum.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED_VALUE.saveValue(playbackSpeed); - - // TODO: extract these strings into localized file - ReVancedUtils.showToastLong("Remembering playback speed: " + playbackSpeed + "x"); - } else if (playbackSpeed != (float) SettingsEnum.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED_VALUE.defaultValue) { - ReVancedUtils.showToastLong("Applying playback speed: " + playbackSpeed + "x"); + if (SettingsEnum.PLAYBACK_SPEED_REMEMBER_LAST_SELECTED.getBoolean()) { + SettingsEnum.PLAYBACK_SPEED_DEFAULT.saveValue(playbackSpeed); + ReVancedUtils.showToastLong("Changed default speed to: " + playbackSpeed + "x"); } } @@ -48,4 +51,27 @@ public final class RememberPlaybackSpeedPatch { public static float getPlaybackSpeedOverride() { return VideoInformation.getCurrentPlaybackSpeed(); } + + /** + * Initialize a settings preference list. + * + * Normally this is done during patching by creating a static xml preference list, + * but the available playback speeds differ depending if {@link CustomVideoSpeedPatch} is applied or not. + */ + public static void initializeListPreference(ListPreference preference) { + if (preferenceListEntries == null) { + float[] videoSpeeds = CustomVideoSpeedPatch.videoSpeeds; + preferenceListEntries = new String[videoSpeeds.length]; + preferenceListEntryValues = new String[videoSpeeds.length]; + int i = 0; + for (float speed : videoSpeeds) { + String speedString = String.valueOf(speed); + preferenceListEntries[i] = speedString + "x"; + preferenceListEntryValues[i] = speedString; + i++; + } + } + preference.setEntries(preferenceListEntries); + preference.setEntryValues(preferenceListEntryValues); + } } diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 8819bd6e..2ddad19d 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -29,9 +29,11 @@ public enum SettingsEnum { // Video settings OLD_STYLE_VIDEO_QUALITY_PLAYER_SETTINGS("revanced_use_old_style_quality_settings", BOOLEAN, TRUE), - REMEMBER_VIDEO_QUALITY_LAST_SELECTED("revanced_remember_video_quality_last_selected", BOOLEAN, TRUE), - REMEMBER_PLAYBACK_SPEED_LAST_SELECTED("revanced_remember_playback_speed_last_selected", BOOLEAN, TRUE), - REMEMBER_PLAYBACK_SPEED_LAST_SELECTED_VALUE("revanced_remember_playback_speed_last_selected_value", FLOAT, 1.0f), + VIDEO_QUALITY_REMEMBER_LAST_SELECTED("revanced_remember_video_quality_last_selected", BOOLEAN, TRUE), + VIDEO_QUALITY_DEFAULT_WIFI("revanced_default_video_quality_wifi", INTEGER, -2), + VIDEO_QUALITY_DEFAULT_MOBILE("revanced_default_video_quality_mobile", INTEGER, -2), + PLAYBACK_SPEED_REMEMBER_LAST_SELECTED("revanced_remember_playback_speed_last_selected", BOOLEAN, TRUE), + PLAYBACK_SPEED_DEFAULT("revanced_default_playback_speed", FLOAT, 1.0f), // TODO: Unused currently // Whitelist settings @@ -295,13 +297,13 @@ public enum SettingsEnum { value = sharedPref.getBoolean(path, (boolean) defaultValue); break; case INTEGER: - value = sharedPref.getInt(path, (Integer) defaultValue); + value = sharedPref.getIntegerString(path, (Integer) defaultValue); break; case LONG: - value = sharedPref.getLong(path, (Long) defaultValue); + value = sharedPref.getLongString(path, (Long) defaultValue); break; case FLOAT: - value = sharedPref.getFloat(path, (Float) defaultValue); + value = sharedPref.getFloatString(path, (Float) defaultValue); break; case STRING: value = sharedPref.getString(path, (String) defaultValue); @@ -316,9 +318,37 @@ public enum SettingsEnum { * * This intentionally is a static method, to deter accidental usage * when {@link #saveValue(Object)} was intended. + * + * This method is only to be used by the Settings preference code. */ - public static void setValue(@NonNull SettingsEnum setting, @NonNull Object newValue) { - setting.value = Objects.requireNonNull(newValue); + public static void setValue(@NonNull SettingsEnum setting, @NonNull String newValue) { + Objects.requireNonNull(newValue); + switch (setting.returnType) { + case BOOLEAN: + setting.value = Boolean.valueOf(newValue); + break; + case INTEGER: + setting.value = Integer.valueOf(newValue); + break; + case LONG: + setting.value = Long.valueOf(newValue); + break; + case FLOAT: + setting.value = Float.valueOf(newValue); + break; + case STRING: + setting.value = newValue; + break; + default: + throw new IllegalStateException(setting.name()); + } + } + /** + * This method is only to be used by the Settings preference code. + */ + public static void setValue(@NonNull SettingsEnum setting, @NonNull Boolean newValue) { + Objects.requireNonNull(newValue); + setting.value = newValue; } /** @@ -331,13 +361,13 @@ public enum SettingsEnum { sharedPref.saveBoolean(path, (boolean) newValue); break; case INTEGER: - sharedPref.saveInt(path, (int) newValue); + sharedPref.saveIntegerString(path, (Integer) newValue); break; case LONG: - sharedPref.saveLong(path, (long) newValue); + sharedPref.saveLongString(path, (Long) newValue); break; case FLOAT: - sharedPref.saveFloat(path, (float) newValue); + sharedPref.saveFloatString(path, (Float) newValue); break; case STRING: sharedPref.saveString(path, (String) newValue); @@ -384,6 +414,14 @@ public enum SettingsEnum { return (String) value; } + /** + * @return the value of this setting as as generic object type. + */ + @NonNull + public Object getObjectValue() { + return value; + } + public enum ReturnType { BOOLEAN, INTEGER, diff --git a/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java b/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java index 59413da3..4bf180ed 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java +++ b/app/src/main/java/app/revanced/integrations/settings/SharedPrefCategory.java @@ -4,11 +4,21 @@ import android.content.Context; import android.content.SharedPreferences; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.Objects; import app.revanced.integrations.utils.ReVancedUtils; +/** + * Shared categories, and helper methods. + * + * The various save methods store numbers as Strings, + * which is required if using {@link android.preference.PreferenceFragment}. + * + * If saved numbers will not be used with a preference fragment, + * then store the primitive numbers using {@link #preferences}. + */ public enum SharedPrefCategory { YOUTUBE("youtube"), RETURN_YOUTUBE_DISLIKE("ryd"), @@ -25,27 +35,41 @@ public enum SharedPrefCategory { preferences = Objects.requireNonNull(ReVancedUtils.getContext()).getSharedPreferences(prefName, Context.MODE_PRIVATE); } - public void saveString(@NonNull String key, @NonNull String value) { - Objects.requireNonNull(value); - preferences.edit().putString(key, value).apply(); + private void saveObjectAsString(@NonNull String key, @Nullable Object value) { + preferences.edit().putString(key, (value == null ? null : value.toString())).apply(); } public void saveBoolean(@NonNull String key, boolean value) { preferences.edit().putBoolean(key, value).apply(); } - public void saveInt(@NonNull String key, int value) { - preferences.edit().putInt(key, value).apply(); + /** + * @param value a NULL parameter removes the value from the preferences + */ + public void saveIntegerString(@NonNull String key, @Nullable Integer value) { + saveObjectAsString(key, value); } - public void saveLong(@NonNull String key, long value) { - preferences.edit().putLong(key, value).apply(); + /** + * @param value a NULL parameter removes the value from the preferences + */ + public void saveLongString(@NonNull String key, @Nullable Long value) { + saveObjectAsString(key, value); } - public void saveFloat(@NonNull String key, float value) { - preferences.edit().putFloat(key, value).apply(); + /** + * @param value a NULL parameter removes the value from the preferences + */ + public void saveFloatString(@NonNull String key, @Nullable Float value) { + saveObjectAsString(key, value); } + /** + * @param value a NULL parameter removes the value from the preferences + */ + public void saveString(@NonNull String key, @Nullable String value) { + saveObjectAsString(key, value); + } @NonNull public String getString(@NonNull String key, @NonNull String _default) { @@ -53,42 +77,50 @@ public enum SharedPrefCategory { return preferences.getString(key, _default); } + public boolean getBoolean(@NonNull String key, boolean _default) { return preferences.getBoolean(key, _default); } - // region Hack, required for PreferencesFragments to function correctly. unknown why required - @NonNull - public Integer getInt(@NonNull String key, @NonNull Integer _default) { + public Integer getIntegerString(@NonNull String key, @NonNull Integer _default) { try { - return Integer.valueOf(preferences.getString(key, _default.toString())); + String value = preferences.getString(key, null); + if (value != null) { + return Integer.valueOf(value); + } + return _default; } catch (ClassCastException ex) { - return preferences.getInt(key, _default); + return preferences.getInt(key, _default); // old data, previously stored as primitive } } @NonNull - public Long getLong(@NonNull String key, @NonNull Long _default) { + public Long getLongString(@NonNull String key, @NonNull Long _default) { try { - return Long.valueOf(preferences.getString(key, _default.toString())); + String value = preferences.getString(key, null); + if (value != null) { + return Long.valueOf(value); + } + return _default; } catch (ClassCastException ex) { return preferences.getLong(key, _default); } } @NonNull - public Float getFloat(@NonNull String key, @NonNull Float _default) { + public Float getFloatString(@NonNull String key, @NonNull Float _default) { try { - return Float.valueOf(preferences.getString(key, _default.toString())); + String value = preferences.getString(key, null); + if (value != null) { + return Float.valueOf(value); + } + return _default; } catch (ClassCastException ex) { return preferences.getFloat(key, _default); } } - // endregion - - @NonNull @Override public String toString() { diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/ReVancedSettingsFragment.java b/app/src/main/java/app/revanced/integrations/settingsmenu/ReVancedSettingsFragment.java index 8862f88e..16b427d2 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/ReVancedSettingsFragment.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/ReVancedSettingsFragment.java @@ -13,6 +13,7 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.os.Process; import android.preference.EditTextPreference; +import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; @@ -23,12 +24,18 @@ import androidx.annotation.Nullable; import com.google.android.apps.youtube.app.application.Shell_HomeActivity; +import app.revanced.integrations.patches.playback.speed.RememberPlaybackSpeedPatch; import app.revanced.integrations.settings.SettingsEnum; import app.revanced.integrations.settings.SharedPrefCategory; import app.revanced.integrations.utils.LogHelper; import app.revanced.integrations.utils.ReVancedUtils; public class ReVancedSettingsFragment extends PreferenceFragment { + /** + * Used to prevent showing reboot dialog, if user cancels a setting user dialog. + */ + private boolean showingUserDialogMessage; + SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> { try { SettingsEnum setting = SettingsEnum.settingFromPath(str); @@ -43,32 +50,22 @@ public class ReVancedSettingsFragment extends PreferenceFragment { SettingsEnum.setValue(setting, switchPref.isChecked()); } else if (pref instanceof EditTextPreference) { String editText = ((EditTextPreference) pref).getText(); - Object value; - switch (setting.returnType) { - case INTEGER: - value = Integer.parseInt(editText); - break; - case LONG: - value = Long.parseLong(editText); - break; - case FLOAT: - value = Float.parseFloat(editText); - break; - case STRING: - value = editText; - break; - default: - throw new IllegalStateException(setting.toString()); - } - SettingsEnum.setValue(setting, value); + SettingsEnum.setValue(setting, editText); + } else if (pref instanceof ListPreference) { + ListPreference listPref = (ListPreference) pref; + SettingsEnum.setValue(setting, listPref.getValue()); + updateListPreferenceSummary((ListPreference) pref, setting); } else { LogHelper.printException(() -> "Setting cannot be handled: " + pref.getClass() + " " + pref); + return; } - if (setting.userDialogMessage != null && ((SwitchPreference) pref).isChecked() != (Boolean) setting.defaultValue) { - showSettingUserDialogConfirmation(getActivity(), (SwitchPreference) pref, setting); - } else if (setting.rebootApp) { - rebootDialog(getActivity()); + if (!showingUserDialogMessage) { + if (setting.userDialogMessage != null && ((SwitchPreference) pref).isChecked() != (Boolean) setting.defaultValue) { + showSettingUserDialogConfirmation(getActivity(), (SwitchPreference) pref, setting); + } else if (setting.rebootApp) { + rebootDialog(getActivity()); + } } enableDisablePreferences(); @@ -88,6 +85,20 @@ public class ReVancedSettingsFragment extends PreferenceFragment { enableDisablePreferences(); + // if the preference was included, then initialize it based on the available playback speed + Preference defaultSpeedPreference = findPreference(SettingsEnum.PLAYBACK_SPEED_DEFAULT.path); + if (defaultSpeedPreference instanceof ListPreference) { + RememberPlaybackSpeedPatch.initializeListPreference((ListPreference) defaultSpeedPreference); + } + + // set the summary text for any ListPreferences + for (SettingsEnum setting : SettingsEnum.values()) { + Preference preference = findPreference(setting.path); + if (preference instanceof ListPreference) { + updateListPreferenceSummary((ListPreference) preference, setting); + } + } + preferenceManager.getSharedPreferences().registerOnSharedPreferenceChangeListener(listener); } catch (Exception ex) { LogHelper.printException(() -> "onActivityCreated() error", ex); @@ -109,6 +120,13 @@ public class ReVancedSettingsFragment extends PreferenceFragment { } } + private void updateListPreferenceSummary(ListPreference listPreference, SettingsEnum setting) { + final int entryIndex = listPreference.findIndexOfValue(setting.getObjectValue().toString()); + if (entryIndex >= 0) { + listPreference.setSummary(listPreference.getEntries()[entryIndex]); + } + } + private void reboot(@NonNull Activity activity) { final int intentFlags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE; PendingIntent intent = PendingIntent.getActivity(activity, 0, @@ -126,10 +144,12 @@ public class ReVancedSettingsFragment extends PreferenceFragment { reboot(activity); }) .setNegativeButton(negativeButton, null) + .setCancelable(false) .show(); } private void showSettingUserDialogConfirmation(@NonNull Activity activity, SwitchPreference switchPref, SettingsEnum setting) { + showingUserDialogMessage = true; new AlertDialog.Builder(activity) .setTitle(str("revanced_settings_confirm_user_dialog_title")) .setMessage(setting.userDialogMessage.toString()) @@ -143,6 +163,10 @@ public class ReVancedSettingsFragment extends PreferenceFragment { SettingsEnum.setValue(setting, defaultBooleanValue); switchPref.setChecked(defaultBooleanValue); }) + .setOnDismissListener(dialog -> { + showingUserDialogMessage = false; + }) + .setCancelable(false) .show(); } }