feat(youtube): user selectable default video speed and quality (#354)

Co-authored-by: johnconner122 <107796137+johnconner122@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
LisoUseInAIKyrios 2023-04-14 05:50:16 +04:00 committed by oSumAtrIX
parent 5ba4cbd4e0
commit 14223f40b5
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
6 changed files with 321 additions and 169 deletions

View File

@ -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<Integer> 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<Integer> 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;
}
}
}

View File

@ -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};
}

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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() {

View File

@ -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();
}
}