mirror of
https://github.com/revanced/revanced-integrations.git
synced 2025-01-20 08:47:33 +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;
|
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.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.List;
|
||||||
|
|
||||||
import app.revanced.integrations.settings.SettingsEnum;
|
import app.revanced.integrations.settings.SettingsEnum;
|
||||||
import app.revanced.integrations.settings.SharedPrefCategory;
|
|
||||||
import app.revanced.integrations.utils.LogHelper;
|
import app.revanced.integrations.utils.LogHelper;
|
||||||
import app.revanced.integrations.utils.ReVancedUtils;
|
import app.revanced.integrations.utils.ReVancedUtils;
|
||||||
import app.revanced.integrations.utils.ReVancedUtils.NetworkType;
|
|
||||||
|
|
||||||
public class RememberVideoQualityPatch {
|
public class RememberVideoQualityPatch {
|
||||||
public static int selectedQuality1 = -2;
|
private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
|
||||||
private static Boolean newVideo = false;
|
private static final SettingsEnum wifiQualitySetting = SettingsEnum.VIDEO_QUALITY_DEFAULT_WIFI;
|
||||||
private static Boolean userChangedQuality = false;
|
private static final SettingsEnum mobileQualitySetting = SettingsEnum.VIDEO_QUALITY_DEFAULT_MOBILE;
|
||||||
|
|
||||||
public static void changeDefaultQuality(int defaultQuality) {
|
private static boolean qualityNeedsUpdating;
|
||||||
Context context = ReVancedUtils.getContext();
|
@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) {
|
if (networkType == NetworkType.NONE) {
|
||||||
ReVancedUtils.showToastShort("No internet connection.");
|
ReVancedUtils.showToastShort("No internet connection");
|
||||||
} else {
|
return;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
String networkTypeMessage;
|
||||||
userChangedQuality = false;
|
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;
|
* Injection point.
|
||||||
|
*
|
||||||
if (!(newVideo || userChangedQuality) || qInterface == null) {
|
* @param qualities Video qualities available, ordered from largest to smallest, with index 0 being the 'automatic' value of -2
|
||||||
return quality;
|
* @param originalQualityIndex quality index to use, as chosen by YouTube
|
||||||
}
|
*/
|
||||||
|
public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) {
|
||||||
Class<?> intType = Integer.TYPE;
|
|
||||||
ArrayList<Integer> iStreamQualities = new ArrayList<>();
|
|
||||||
try {
|
try {
|
||||||
for (Object streamQuality : qualities) {
|
if (!(qualityNeedsUpdating || userChangedDefaultQuality) || qInterface == null) {
|
||||||
for (Field field : streamQuality.getClass().getFields()) {
|
return originalQualityIndex;
|
||||||
if (field.getType().isAssignableFrom(intType)) { // converts quality index to actual readable resolution
|
}
|
||||||
int value = field.getInt(streamQuality);
|
qualityNeedsUpdating = false;
|
||||||
if (field.getName().length() <= 2) {
|
|
||||||
iStreamQualities.add(value);
|
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) {
|
|
||||||
}
|
if (userChangedDefaultQuality) {
|
||||||
Collections.sort(iStreamQualities);
|
userChangedDefaultQuality = false;
|
||||||
int index = 0;
|
final int quality = videoQualities.get(userSelectedQualityIndex);
|
||||||
if (userChangedQuality) {
|
LogHelper.printDebug(() -> "User changed default quality to: " + quality);
|
||||||
for (int convertedQuality : iStreamQualities) {
|
changeDefaultQuality(quality);
|
||||||
int selectedQuality2 = qualities.length - selectedQuality1 + 1;
|
return userSelectedQualityIndex;
|
||||||
index++;
|
}
|
||||||
if (selectedQuality2 == index) {
|
|
||||||
final int indexToLog = index; // must be final for lambda
|
// find the highest quality that is equal to or less than the preferred
|
||||||
LogHelper.printDebug(() -> "Quality index is: " + indexToLog + " and corresponding value is: " + convertedQuality);
|
int qualityToUse = videoQualities.get(0); // first element is automatic mode
|
||||||
changeDefaultQuality(convertedQuality);
|
int qualityIndexToUse = 0;
|
||||||
return selectedQuality2;
|
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);
|
final int qualityToUseLog = qualityToUse;
|
||||||
if (preferredQuality == -2) return quality;
|
LogHelper.printDebug(() -> "Quality changed from: "
|
||||||
|
+ videoQualities.get(originalQualityIndex) + " to: " + qualityToUseLog);
|
||||||
|
|
||||||
for (int streamQuality2 : iStreamQualities) {
|
Method m = qInterface.getClass().getMethod(qIndexMethod, Integer.TYPE);
|
||||||
final int indexToLog = index;
|
m.invoke(qInterface, qualityToUse);
|
||||||
LogHelper.printDebug(() -> "Quality at index " + indexToLog + ": " + streamQuality2);
|
return qualityIndexToUse;
|
||||||
index++;
|
} catch (Exception ex) {
|
||||||
}
|
LogHelper.printException(() -> "Failed to set quality", ex);
|
||||||
for (Integer iStreamQuality : iStreamQualities) {
|
return originalQualityIndex;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
public static void userChangedQuality(int selectedQuality) {
|
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;
|
userSelectedQualityIndex = selectedQuality;
|
||||||
userChangedQuality = true;
|
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;
|
package app.revanced.integrations.patches.playback.speed;
|
||||||
|
|
||||||
public class CustomVideoSpeedPatch {
|
public class CustomVideoSpeedPatch {
|
||||||
// Values are useless as they are being overridden by the respective patch.
|
/**
|
||||||
// This generates a .array segment in Dalvik bytecode
|
* Default playback speeds offered by YouTube.
|
||||||
// which the patch utilizes to store the video speeds in, only
|
* Values are also used by {@link RememberPlaybackSpeedPatch}.
|
||||||
// if it has two or more default values.
|
*
|
||||||
public static final float[] videoSpeeds = { 0, 0 };
|
* 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;
|
package app.revanced.integrations.patches.playback.speed;
|
||||||
|
|
||||||
|
import android.preference.ListPreference;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@ -9,6 +11,11 @@ import app.revanced.integrations.utils.ReVancedUtils;
|
|||||||
|
|
||||||
public final class RememberPlaybackSpeedPatch {
|
public final class RememberPlaybackSpeedPatch {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PreferenceList entries and values, of all available playback speeds.
|
||||||
|
*/
|
||||||
|
private static String[] preferenceListEntries, preferenceListEntryValues;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static String currentVideoId;
|
private static String currentVideoId;
|
||||||
|
|
||||||
@ -21,7 +28,7 @@ public final class RememberPlaybackSpeedPatch {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentVideoId = videoId;
|
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
|
* @param playbackSpeed The playback speed the user selected
|
||||||
*/
|
*/
|
||||||
public static void userSelectedPlaybackSpeed(float playbackSpeed) {
|
public static void userSelectedPlaybackSpeed(float playbackSpeed) {
|
||||||
if (SettingsEnum.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED.getBoolean()) {
|
if (SettingsEnum.PLAYBACK_SPEED_REMEMBER_LAST_SELECTED.getBoolean()) {
|
||||||
SettingsEnum.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED_VALUE.saveValue(playbackSpeed);
|
SettingsEnum.PLAYBACK_SPEED_DEFAULT.saveValue(playbackSpeed);
|
||||||
|
ReVancedUtils.showToastLong("Changed default speed to: " + playbackSpeed + "x");
|
||||||
// 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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,4 +51,27 @@ public final class RememberPlaybackSpeedPatch {
|
|||||||
public static float getPlaybackSpeedOverride() {
|
public static float getPlaybackSpeedOverride() {
|
||||||
return VideoInformation.getCurrentPlaybackSpeed();
|
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
|
// Video settings
|
||||||
OLD_STYLE_VIDEO_QUALITY_PLAYER_SETTINGS("revanced_use_old_style_quality_settings", BOOLEAN, TRUE),
|
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),
|
VIDEO_QUALITY_REMEMBER_LAST_SELECTED("revanced_remember_video_quality_last_selected", BOOLEAN, TRUE),
|
||||||
REMEMBER_PLAYBACK_SPEED_LAST_SELECTED("revanced_remember_playback_speed_last_selected", BOOLEAN, TRUE),
|
VIDEO_QUALITY_DEFAULT_WIFI("revanced_default_video_quality_wifi", INTEGER, -2),
|
||||||
REMEMBER_PLAYBACK_SPEED_LAST_SELECTED_VALUE("revanced_remember_playback_speed_last_selected_value", FLOAT, 1.0f),
|
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
|
// TODO: Unused currently
|
||||||
// Whitelist settings
|
// Whitelist settings
|
||||||
@ -295,13 +297,13 @@ public enum SettingsEnum {
|
|||||||
value = sharedPref.getBoolean(path, (boolean) defaultValue);
|
value = sharedPref.getBoolean(path, (boolean) defaultValue);
|
||||||
break;
|
break;
|
||||||
case INTEGER:
|
case INTEGER:
|
||||||
value = sharedPref.getInt(path, (Integer) defaultValue);
|
value = sharedPref.getIntegerString(path, (Integer) defaultValue);
|
||||||
break;
|
break;
|
||||||
case LONG:
|
case LONG:
|
||||||
value = sharedPref.getLong(path, (Long) defaultValue);
|
value = sharedPref.getLongString(path, (Long) defaultValue);
|
||||||
break;
|
break;
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
value = sharedPref.getFloat(path, (Float) defaultValue);
|
value = sharedPref.getFloatString(path, (Float) defaultValue);
|
||||||
break;
|
break;
|
||||||
case STRING:
|
case STRING:
|
||||||
value = sharedPref.getString(path, (String) defaultValue);
|
value = sharedPref.getString(path, (String) defaultValue);
|
||||||
@ -316,9 +318,37 @@ public enum SettingsEnum {
|
|||||||
*
|
*
|
||||||
* This intentionally is a static method, to deter accidental usage
|
* This intentionally is a static method, to deter accidental usage
|
||||||
* when {@link #saveValue(Object)} was intended.
|
* 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) {
|
public static void setValue(@NonNull SettingsEnum setting, @NonNull String newValue) {
|
||||||
setting.value = Objects.requireNonNull(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);
|
sharedPref.saveBoolean(path, (boolean) newValue);
|
||||||
break;
|
break;
|
||||||
case INTEGER:
|
case INTEGER:
|
||||||
sharedPref.saveInt(path, (int) newValue);
|
sharedPref.saveIntegerString(path, (Integer) newValue);
|
||||||
break;
|
break;
|
||||||
case LONG:
|
case LONG:
|
||||||
sharedPref.saveLong(path, (long) newValue);
|
sharedPref.saveLongString(path, (Long) newValue);
|
||||||
break;
|
break;
|
||||||
case FLOAT:
|
case FLOAT:
|
||||||
sharedPref.saveFloat(path, (float) newValue);
|
sharedPref.saveFloatString(path, (Float) newValue);
|
||||||
break;
|
break;
|
||||||
case STRING:
|
case STRING:
|
||||||
sharedPref.saveString(path, (String) newValue);
|
sharedPref.saveString(path, (String) newValue);
|
||||||
@ -384,6 +414,14 @@ public enum SettingsEnum {
|
|||||||
return (String) value;
|
return (String) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the value of this setting as as generic object type.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Object getObjectValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
public enum ReturnType {
|
public enum ReturnType {
|
||||||
BOOLEAN,
|
BOOLEAN,
|
||||||
INTEGER,
|
INTEGER,
|
||||||
|
@ -4,11 +4,21 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import app.revanced.integrations.utils.ReVancedUtils;
|
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 {
|
public enum SharedPrefCategory {
|
||||||
YOUTUBE("youtube"),
|
YOUTUBE("youtube"),
|
||||||
RETURN_YOUTUBE_DISLIKE("ryd"),
|
RETURN_YOUTUBE_DISLIKE("ryd"),
|
||||||
@ -25,27 +35,41 @@ public enum SharedPrefCategory {
|
|||||||
preferences = Objects.requireNonNull(ReVancedUtils.getContext()).getSharedPreferences(prefName, Context.MODE_PRIVATE);
|
preferences = Objects.requireNonNull(ReVancedUtils.getContext()).getSharedPreferences(prefName, Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveString(@NonNull String key, @NonNull String value) {
|
private void saveObjectAsString(@NonNull String key, @Nullable Object value) {
|
||||||
Objects.requireNonNull(value);
|
preferences.edit().putString(key, (value == null ? null : value.toString())).apply();
|
||||||
preferences.edit().putString(key, value).apply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveBoolean(@NonNull String key, boolean value) {
|
public void saveBoolean(@NonNull String key, boolean value) {
|
||||||
preferences.edit().putBoolean(key, value).apply();
|
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
|
@NonNull
|
||||||
public String getString(@NonNull String key, @NonNull String _default) {
|
public String getString(@NonNull String key, @NonNull String _default) {
|
||||||
@ -53,42 +77,50 @@ public enum SharedPrefCategory {
|
|||||||
return preferences.getString(key, _default);
|
return preferences.getString(key, _default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean getBoolean(@NonNull String key, boolean _default) {
|
public boolean getBoolean(@NonNull String key, boolean _default) {
|
||||||
return preferences.getBoolean(key, _default);
|
return preferences.getBoolean(key, _default);
|
||||||
}
|
}
|
||||||
|
|
||||||
// region Hack, required for PreferencesFragments to function correctly. unknown why required
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public Integer getInt(@NonNull String key, @NonNull Integer _default) {
|
public Integer getIntegerString(@NonNull String key, @NonNull Integer _default) {
|
||||||
try {
|
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) {
|
} catch (ClassCastException ex) {
|
||||||
return preferences.getInt(key, _default);
|
return preferences.getInt(key, _default); // old data, previously stored as primitive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public Long getLong(@NonNull String key, @NonNull Long _default) {
|
public Long getLongString(@NonNull String key, @NonNull Long _default) {
|
||||||
try {
|
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) {
|
} catch (ClassCastException ex) {
|
||||||
return preferences.getLong(key, _default);
|
return preferences.getLong(key, _default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public Float getFloat(@NonNull String key, @NonNull Float _default) {
|
public Float getFloatString(@NonNull String key, @NonNull Float _default) {
|
||||||
try {
|
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) {
|
} catch (ClassCastException ex) {
|
||||||
return preferences.getFloat(key, _default);
|
return preferences.getFloat(key, _default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -13,6 +13,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.preference.EditTextPreference;
|
import android.preference.EditTextPreference;
|
||||||
|
import android.preference.ListPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
@ -23,12 +24,18 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import com.google.android.apps.youtube.app.application.Shell_HomeActivity;
|
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.SettingsEnum;
|
||||||
import app.revanced.integrations.settings.SharedPrefCategory;
|
import app.revanced.integrations.settings.SharedPrefCategory;
|
||||||
import app.revanced.integrations.utils.LogHelper;
|
import app.revanced.integrations.utils.LogHelper;
|
||||||
import app.revanced.integrations.utils.ReVancedUtils;
|
import app.revanced.integrations.utils.ReVancedUtils;
|
||||||
|
|
||||||
public class ReVancedSettingsFragment extends PreferenceFragment {
|
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) -> {
|
SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
|
||||||
try {
|
try {
|
||||||
SettingsEnum setting = SettingsEnum.settingFromPath(str);
|
SettingsEnum setting = SettingsEnum.settingFromPath(str);
|
||||||
@ -43,32 +50,22 @@ public class ReVancedSettingsFragment extends PreferenceFragment {
|
|||||||
SettingsEnum.setValue(setting, switchPref.isChecked());
|
SettingsEnum.setValue(setting, switchPref.isChecked());
|
||||||
} else if (pref instanceof EditTextPreference) {
|
} else if (pref instanceof EditTextPreference) {
|
||||||
String editText = ((EditTextPreference) pref).getText();
|
String editText = ((EditTextPreference) pref).getText();
|
||||||
Object value;
|
SettingsEnum.setValue(setting, editText);
|
||||||
switch (setting.returnType) {
|
} else if (pref instanceof ListPreference) {
|
||||||
case INTEGER:
|
ListPreference listPref = (ListPreference) pref;
|
||||||
value = Integer.parseInt(editText);
|
SettingsEnum.setValue(setting, listPref.getValue());
|
||||||
break;
|
updateListPreferenceSummary((ListPreference) pref, setting);
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
LogHelper.printException(() -> "Setting cannot be handled: " + pref.getClass() + " " + pref);
|
LogHelper.printException(() -> "Setting cannot be handled: " + pref.getClass() + " " + pref);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setting.userDialogMessage != null && ((SwitchPreference) pref).isChecked() != (Boolean) setting.defaultValue) {
|
if (!showingUserDialogMessage) {
|
||||||
showSettingUserDialogConfirmation(getActivity(), (SwitchPreference) pref, setting);
|
if (setting.userDialogMessage != null && ((SwitchPreference) pref).isChecked() != (Boolean) setting.defaultValue) {
|
||||||
} else if (setting.rebootApp) {
|
showSettingUserDialogConfirmation(getActivity(), (SwitchPreference) pref, setting);
|
||||||
rebootDialog(getActivity());
|
} else if (setting.rebootApp) {
|
||||||
|
rebootDialog(getActivity());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enableDisablePreferences();
|
enableDisablePreferences();
|
||||||
@ -88,6 +85,20 @@ public class ReVancedSettingsFragment extends PreferenceFragment {
|
|||||||
|
|
||||||
enableDisablePreferences();
|
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);
|
preferenceManager.getSharedPreferences().registerOnSharedPreferenceChangeListener(listener);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LogHelper.printException(() -> "onActivityCreated() error", 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) {
|
private void reboot(@NonNull Activity activity) {
|
||||||
final int intentFlags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
|
final int intentFlags = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
|
||||||
PendingIntent intent = PendingIntent.getActivity(activity, 0,
|
PendingIntent intent = PendingIntent.getActivity(activity, 0,
|
||||||
@ -126,10 +144,12 @@ public class ReVancedSettingsFragment extends PreferenceFragment {
|
|||||||
reboot(activity);
|
reboot(activity);
|
||||||
})
|
})
|
||||||
.setNegativeButton(negativeButton, null)
|
.setNegativeButton(negativeButton, null)
|
||||||
|
.setCancelable(false)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSettingUserDialogConfirmation(@NonNull Activity activity, SwitchPreference switchPref, SettingsEnum setting) {
|
private void showSettingUserDialogConfirmation(@NonNull Activity activity, SwitchPreference switchPref, SettingsEnum setting) {
|
||||||
|
showingUserDialogMessage = true;
|
||||||
new AlertDialog.Builder(activity)
|
new AlertDialog.Builder(activity)
|
||||||
.setTitle(str("revanced_settings_confirm_user_dialog_title"))
|
.setTitle(str("revanced_settings_confirm_user_dialog_title"))
|
||||||
.setMessage(setting.userDialogMessage.toString())
|
.setMessage(setting.userDialogMessage.toString())
|
||||||
@ -143,6 +163,10 @@ public class ReVancedSettingsFragment extends PreferenceFragment {
|
|||||||
SettingsEnum.setValue(setting, defaultBooleanValue);
|
SettingsEnum.setValue(setting, defaultBooleanValue);
|
||||||
switchPref.setChecked(defaultBooleanValue);
|
switchPref.setChecked(defaultBooleanValue);
|
||||||
})
|
})
|
||||||
|
.setOnDismissListener(dialog -> {
|
||||||
|
showingUserDialogMessage = false;
|
||||||
|
})
|
||||||
|
.setCancelable(false)
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user