mirror of
https://github.com/revanced/revanced-patches
synced 2024-11-30 12:02:58 +01:00
fix(YouTube - Playback speed): Restore old playback speed menu (#3817)
This commit is contained in:
parent
5988b75975
commit
806b21093e
@ -3,25 +3,48 @@ package app.revanced.extension.youtube.patches.components;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
|
||||||
|
import app.revanced.extension.youtube.settings.Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abuse LithoFilter for {@link CustomPlaybackSpeedPatch}.
|
* Abuse LithoFilter for {@link CustomPlaybackSpeedPatch}.
|
||||||
*/
|
*/
|
||||||
public final class PlaybackSpeedMenuFilterPatch extends Filter {
|
public final class PlaybackSpeedMenuFilterPatch extends Filter {
|
||||||
// Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread.
|
|
||||||
public static volatile boolean isPlaybackSpeedMenuVisible;
|
/**
|
||||||
|
* Old litho based speed selection menu.
|
||||||
|
*/
|
||||||
|
public static volatile boolean isOldPlaybackSpeedMenuVisible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 0.05x speed selection menu.
|
||||||
|
*/
|
||||||
|
public static volatile boolean isPlaybackRateSelectorMenuVisible;
|
||||||
|
|
||||||
|
private final StringFilterGroup oldPlaybackMenuGroup;
|
||||||
|
|
||||||
public PlaybackSpeedMenuFilterPatch() {
|
public PlaybackSpeedMenuFilterPatch() {
|
||||||
addPathCallbacks(new StringFilterGroup(
|
// 0.05x litho speed menu.
|
||||||
null,
|
var playbackRateSelectorGroup = new StringFilterGroup(
|
||||||
"playback_speed_sheet_content.eml-js"
|
Settings.CUSTOM_SPEED_MENU,
|
||||||
));
|
"playback_rate_selector_menu_sheet.eml-js"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Old litho based speed menu.
|
||||||
|
oldPlaybackMenuGroup = new StringFilterGroup(
|
||||||
|
Settings.CUSTOM_SPEED_MENU,
|
||||||
|
"playback_speed_sheet_content.eml-js");
|
||||||
|
|
||||||
|
addPathCallbacks(playbackRateSelectorGroup, oldPlaybackMenuGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
|
||||||
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
|
||||||
isPlaybackSpeedMenuVisible = true;
|
if (matchedGroup == oldPlaybackMenuGroup) {
|
||||||
|
isOldPlaybackSpeedMenuVisible = true;
|
||||||
|
} else {
|
||||||
|
isPlaybackRateSelectorMenuVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -59,18 +59,24 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
if (speedStrings.length == 0) {
|
if (speedStrings.length == 0) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
customPlaybackSpeeds = new float[speedStrings.length];
|
customPlaybackSpeeds = new float[speedStrings.length];
|
||||||
for (int i = 0, length = speedStrings.length; i < length; i++) {
|
|
||||||
final float speed = Float.parseFloat(speedStrings[i]);
|
int i = 0;
|
||||||
if (speed <= 0 || arrayContains(customPlaybackSpeeds, speed)) {
|
for (String speedString : speedStrings) {
|
||||||
|
final float speedFloat = Float.parseFloat(speedString);
|
||||||
|
if (speedFloat <= 0 || arrayContains(customPlaybackSpeeds, speedFloat)) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
if (speed >= MAXIMUM_PLAYBACK_SPEED) {
|
|
||||||
|
if (speedFloat >= MAXIMUM_PLAYBACK_SPEED) {
|
||||||
resetCustomSpeeds(str("revanced_custom_playback_speeds_invalid", MAXIMUM_PLAYBACK_SPEED));
|
resetCustomSpeeds(str("revanced_custom_playback_speeds_invalid", MAXIMUM_PLAYBACK_SPEED));
|
||||||
loadCustomSpeeds();
|
loadCustomSpeeds();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
customPlaybackSpeeds[i] = speed;
|
|
||||||
|
customPlaybackSpeeds[i] = speedFloat;
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printInfo(() -> "parse error", ex);
|
Logger.printInfo(() -> "parse error", ex);
|
||||||
@ -89,10 +95,12 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
/**
|
/**
|
||||||
* Initialize a settings preference list with the available playback speeds.
|
* Initialize a settings preference list with the available playback speeds.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public static void initializeListPreference(ListPreference preference) {
|
public static void initializeListPreference(ListPreference preference) {
|
||||||
if (preferenceListEntries == null) {
|
if (preferenceListEntries == null) {
|
||||||
preferenceListEntries = new String[customPlaybackSpeeds.length];
|
preferenceListEntries = new String[customPlaybackSpeeds.length];
|
||||||
preferenceListEntryValues = new String[customPlaybackSpeeds.length];
|
preferenceListEntryValues = new String[customPlaybackSpeeds.length];
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (float speed : customPlaybackSpeeds) {
|
for (float speed : customPlaybackSpeeds) {
|
||||||
String speedString = String.valueOf(speed);
|
String speedString = String.valueOf(speed);
|
||||||
@ -101,6 +109,7 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preference.setEntries(preferenceListEntries);
|
preference.setEntries(preferenceListEntries);
|
||||||
preference.setEntryValues(preferenceListEntryValues);
|
preference.setEntryValues(preferenceListEntryValues);
|
||||||
}
|
}
|
||||||
@ -111,54 +120,69 @@ public class CustomPlaybackSpeedPatch {
|
|||||||
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
|
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
|
||||||
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
|
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
|
||||||
try {
|
try {
|
||||||
// For some reason, the custom playback speed flyout panel is activated when the user opens the share panel. (A/B tests)
|
if (PlaybackSpeedMenuFilterPatch.isPlaybackRateSelectorMenuVisible) {
|
||||||
// Check the child count of playback speed flyout panel to prevent this issue.
|
if (hideLithoMenuAndShowOldSpeedMenu(recyclerView, 5)) {
|
||||||
// Child count of playback speed flyout panel is always 8.
|
PlaybackSpeedMenuFilterPatch.isPlaybackRateSelectorMenuVisible = false;
|
||||||
if (!PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible || recyclerView.getChildCount() == 0) {
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
View firstChild = recyclerView.getChildAt(0);
|
|
||||||
if (!(firstChild instanceof ViewGroup)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ViewGroup PlaybackSpeedParentView = (ViewGroup) firstChild;
|
|
||||||
if (PlaybackSpeedParentView.getChildCount() != 8) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible = false;
|
|
||||||
|
|
||||||
ViewParent parentView3rd = Utils.getParentView(recyclerView, 3);
|
|
||||||
if (!(parentView3rd instanceof ViewGroup)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ViewParent parentView4th = parentView3rd.getParent();
|
|
||||||
if (!(parentView4th instanceof ViewGroup)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
|
|
||||||
// This only shows in phone layout.
|
|
||||||
final var touchInsidedView = ((ViewGroup) parentView4th).getChildAt(0);
|
|
||||||
touchInsidedView.setSoundEffectsEnabled(false);
|
|
||||||
touchInsidedView.performClick();
|
|
||||||
|
|
||||||
// In tablet layout there is no Dismiss View, instead we just hide all two parent views.
|
|
||||||
((ViewGroup) parentView3rd).setVisibility(View.GONE);
|
|
||||||
((ViewGroup) parentView4th).setVisibility(View.GONE);
|
|
||||||
|
|
||||||
// This works without issues for both tablet and phone layouts,
|
|
||||||
// So no code is needed to check whether the current device is a tablet or phone.
|
|
||||||
|
|
||||||
// Close the new Playback speed menu and show the old one.
|
|
||||||
showOldPlaybackSpeedMenu();
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onFlyoutMenuCreate failure", ex);
|
Logger.printException(() -> "isPlaybackRateSelectorMenuVisible failure", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (PlaybackSpeedMenuFilterPatch.isOldPlaybackSpeedMenuVisible) {
|
||||||
|
if (hideLithoMenuAndShowOldSpeedMenu(recyclerView, 8)) {
|
||||||
|
PlaybackSpeedMenuFilterPatch.isOldPlaybackSpeedMenuVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "isOldPlaybackSpeedMenuVisible failure", ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean hideLithoMenuAndShowOldSpeedMenu(RecyclerView recyclerView, int expectedChildCount) {
|
||||||
|
if (recyclerView.getChildCount() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
View firstChild = recyclerView.getChildAt(0);
|
||||||
|
if (!(firstChild instanceof ViewGroup)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewGroup PlaybackSpeedParentView = (ViewGroup) firstChild;
|
||||||
|
if (PlaybackSpeedParentView.getChildCount() != expectedChildCount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewParent parentView3rd = Utils.getParentView(recyclerView, 3);
|
||||||
|
if (!(parentView3rd instanceof ViewGroup)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewParent parentView4th = parentView3rd.getParent();
|
||||||
|
if (!(parentView4th instanceof ViewGroup)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
|
||||||
|
// This only shows in phone layout.
|
||||||
|
final var touchInsidedView = ((ViewGroup) parentView4th).getChildAt(0);
|
||||||
|
touchInsidedView.setSoundEffectsEnabled(false);
|
||||||
|
touchInsidedView.performClick();
|
||||||
|
|
||||||
|
// In tablet layout there is no Dismiss View, instead we just hide all two parent views.
|
||||||
|
((ViewGroup) parentView3rd).setVisibility(View.GONE);
|
||||||
|
((ViewGroup) parentView4th).setVisibility(View.GONE);
|
||||||
|
|
||||||
|
// Close the litho speed menu and show the old one.
|
||||||
|
showOldPlaybackSpeedMenu();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static void showOldPlaybackSpeedMenu() {
|
public static void showOldPlaybackSpeedMenu() {
|
||||||
// This method is sometimes used multiple times.
|
// This method is sometimes used multiple times.
|
||||||
// To prevent this, ignore method reuse within 1 second.
|
// To prevent this, ignore method reuse within 1 second.
|
||||||
|
@ -30,17 +30,31 @@ public final class RememberPlaybackSpeedPatch {
|
|||||||
*/
|
*/
|
||||||
public static void userSelectedPlaybackSpeed(float playbackSpeed) {
|
public static void userSelectedPlaybackSpeed(float playbackSpeed) {
|
||||||
if (Settings.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED.get()) {
|
if (Settings.REMEMBER_PLAYBACK_SPEED_LAST_SELECTED.get()) {
|
||||||
Settings.PLAYBACK_SPEED_DEFAULT.save(playbackSpeed);
|
// With the 0.05x menu, if the speed is set by integrations to higher than 2.0x
|
||||||
|
// then the menu will allow increasing without bounds but the max speed is
|
||||||
|
// still capped to under 8.0x.
|
||||||
|
playbackSpeed = Math.min(playbackSpeed, CustomPlaybackSpeedPatch.MAXIMUM_PLAYBACK_SPEED - 0.05f);
|
||||||
|
|
||||||
// Prevent toast spamming if using the 0.05x adjustments.
|
// Prevent toast spamming if using the 0.05x adjustments.
|
||||||
// Show exactly one toast after the user stops interacting with the speed menu.
|
// Show exactly one toast after the user stops interacting with the speed menu.
|
||||||
final long now = System.currentTimeMillis();
|
final long now = System.currentTimeMillis();
|
||||||
lastTimeSpeedChanged = now;
|
lastTimeSpeedChanged = now;
|
||||||
|
|
||||||
|
final float finalPlaybackSpeed = playbackSpeed;
|
||||||
Utils.runOnMainThreadDelayed(() -> {
|
Utils.runOnMainThreadDelayed(() -> {
|
||||||
if (lastTimeSpeedChanged == now) {
|
if (lastTimeSpeedChanged != now) {
|
||||||
Utils.showToastLong(str("revanced_remember_playback_speed_toast", (playbackSpeed + "x")));
|
// The user made additional speed adjustments and this call is outdated.
|
||||||
} // else, the user made additional speed adjustments and this call is outdated.
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.PLAYBACK_SPEED_DEFAULT.get() == finalPlaybackSpeed) {
|
||||||
|
// User changed to a different speed and immediately changed back.
|
||||||
|
// Or the user is going past 8.0x in the glitched out 0.05x menu.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Settings.PLAYBACK_SPEED_DEFAULT.save(finalPlaybackSpeed);
|
||||||
|
|
||||||
|
Utils.showToastLong(str("revanced_remember_playback_speed_toast", (finalPlaybackSpeed + "x")));
|
||||||
}, TOAST_DELAY_MILLISECONDS);
|
}, TOAST_DELAY_MILLISECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,9 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
|
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
|
||||||
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
|
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
|
||||||
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
|
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
|
||||||
|
// Speed
|
||||||
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
|
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
|
||||||
|
public static final BooleanSetting CUSTOM_SPEED_MENU = new BooleanSetting("revanced_custom_speed_menu", TRUE);
|
||||||
public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", 1.0f);
|
public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", 1.0f);
|
||||||
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
|
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
|
||||||
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
|
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
|
||||||
@ -378,3 +380,4 @@ public class Settings extends BaseSettings {
|
|||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import app.revanced.patches.shared.misc.mapping.get
|
|||||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||||
import app.revanced.patches.shared.misc.settings.preference.InputType
|
import app.revanced.patches.shared.misc.settings.preference.InputType
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
|
||||||
@ -71,6 +72,7 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
|
|||||||
addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch")
|
addResources("youtube", "video.speed.custom.customPlaybackSpeedPatch")
|
||||||
|
|
||||||
PreferenceScreen.VIDEO.addPreferences(
|
PreferenceScreen.VIDEO.addPreferences(
|
||||||
|
SwitchPreference("revanced_custom_speed_menu"),
|
||||||
TextPreference("revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE),
|
TextPreference("revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -108,18 +110,18 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
|
|||||||
|
|
||||||
// Override the min/max speeds that can be used.
|
// Override the min/max speeds that can be used.
|
||||||
speedLimiterMatch.mutableMethod.apply {
|
speedLimiterMatch.mutableMethod.apply {
|
||||||
val limiterMinConstIndex = indexOfFirstLiteralInstructionOrThrow(0.25f.toRawBits().toLong())
|
val limitMinIndex = indexOfFirstLiteralInstructionOrThrow(0.25f.toRawBits().toLong())
|
||||||
var limiterMaxConstIndex = indexOfFirstLiteralInstruction(2.0f.toRawBits().toLong())
|
var limitMaxIndex = indexOfFirstLiteralInstruction(2.0f.toRawBits().toLong())
|
||||||
// Newer targets have 4x max speed.
|
// Newer targets have 4x max speed.
|
||||||
if (limiterMaxConstIndex < 0) {
|
if (limitMaxIndex < 0) {
|
||||||
limiterMaxConstIndex = indexOfFirstLiteralInstructionOrThrow(4.0f.toRawBits().toLong())
|
limitMaxIndex = indexOfFirstLiteralInstructionOrThrow(4.0f.toRawBits().toLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
val limiterMinConstDestination = getInstruction<OneRegisterInstruction>(limiterMinConstIndex).registerA
|
val limitMinRegister = getInstruction<OneRegisterInstruction>(limitMinIndex).registerA
|
||||||
val limiterMaxConstDestination = getInstruction<OneRegisterInstruction>(limiterMaxConstIndex).registerA
|
val limitMaxRegister = getInstruction<OneRegisterInstruction>(limitMaxIndex).registerA
|
||||||
|
|
||||||
replaceInstruction(limiterMinConstIndex, "const/high16 v$limiterMinConstDestination, 0.0f")
|
replaceInstruction(limitMinIndex, "const/high16 v$limitMinRegister, 0.0f")
|
||||||
replaceInstruction(limiterMaxConstIndex, "const/high16 v$limiterMaxConstDestination, 10.0f")
|
replaceInstruction(limitMaxIndex, "const/high16 v$limitMaxRegister, 8.0f")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a static INSTANCE field to the class.
|
// Add a static INSTANCE field to the class.
|
||||||
|
@ -1172,8 +1172,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
|||||||
<string name="revanced_playback_speed_dialog_button_summary_off">Button is not shown</string>
|
<string name="revanced_playback_speed_dialog_button_summary_off">Button is not shown</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
<patch id="video.speed.custom.customPlaybackSpeedPatch">
|
||||||
|
<string name="revanced_custom_speed_menu_title">Custom playback speed menu</string>
|
||||||
|
<string name="revanced_custom_speed_menu_summary_on">Custom speed menu is shown</string>
|
||||||
|
<string name="revanced_custom_speed_menu_summary_off">Custom speed menu is not shown</string>
|
||||||
<string name="revanced_custom_playback_speeds_title">Custom playback speeds</string>
|
<string name="revanced_custom_playback_speeds_title">Custom playback speeds</string>
|
||||||
<string name="revanced_custom_playback_speeds_summary">Add or change the available playback speeds</string>
|
<string name="revanced_custom_playback_speeds_summary">Add or change the custom playback speeds</string>
|
||||||
<string name="revanced_custom_playback_speeds_invalid">Custom speeds must be less than %s. Using default values.</string>
|
<string name="revanced_custom_playback_speeds_invalid">Custom speeds must be less than %s. Using default values.</string>
|
||||||
<string name="revanced_custom_playback_speeds_parse_exception">Invalid custom playback speeds. Using default values.</string>
|
<string name="revanced_custom_playback_speeds_parse_exception">Invalid custom playback speeds. Using default values.</string>
|
||||||
</patch>
|
</patch>
|
||||||
|
Loading…
Reference in New Issue
Block a user