mirror of
https://github.com/revanced/revanced-integrations.git
synced 2025-01-02 16:15:58 +01:00
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:
parent
5ba4cbd4e0
commit
14223f40b5
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user