feat(YouTube): Add Snack bar components patch

This commit is contained in:
inotia00 2025-01-22 13:01:31 +09:00
parent e85a343c02
commit 78f1d962cd
14 changed files with 642 additions and 44 deletions

View File

@ -61,7 +61,7 @@ public abstract class Filter {
*/
public boolean skip(String conversionContext, SpannableString spannableString, Object span, int start, int end,
int flags, boolean isWord, SpanType spanType, StringFilterGroup matchedGroup) {
if (BaseSettings.ENABLE_DEBUG_LOGGING.get()) {
if (BaseSettings.ENABLE_DEBUG_BUFFER_LOGGING.get()) {
String filterSimpleName = getClass().getSimpleName();
Logger.printDebug(() -> filterSimpleName + " Removed setSpan: " + spanType.type);
}

View File

@ -188,10 +188,6 @@ public class GeneralPatch {
return Settings.HIDE_FLOATING_MICROPHONE.get() || original;
}
public static boolean hideSnackBar() {
return Settings.HIDE_SNACK_BAR.get();
}
// endregion
// region [Hide navigation bar components] patch

View File

@ -0,0 +1,115 @@
package app.revanced.extension.youtube.patches.general;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.widget.FrameLayout;
import java.util.concurrent.atomic.AtomicBoolean;
import app.revanced.extension.shared.utils.ResourceUtils;
import app.revanced.extension.shared.utils.Utils;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.utils.ThemeUtils;
@SuppressWarnings("unused")
public final class SnackBarPatch {
private static final boolean HIDE_SNACK_BAR =
Settings.HIDE_SNACK_BAR.get();
private static final boolean HIDE_SERVER_SIDE_SNACK_BAR =
Settings.HIDE_SERVER_SIDE_SNACK_BAR.get();
private static final boolean CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND =
!HIDE_SNACK_BAR && !HIDE_SERVER_SIDE_SNACK_BAR && Settings.CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND.get();
private static final boolean INVERT_SNACK_BAR_THEME =
!HIDE_SNACK_BAR && Settings.INVERT_SNACK_BAR_THEME.get();
private static final boolean INVERT_SERVER_SIDE_SNACK_BAR_THEME =
!HIDE_SERVER_SIDE_SNACK_BAR && INVERT_SNACK_BAR_THEME;
private static final int SNACK_BAR_BLACK_COLOR = 0xFF0F0F0F;
private static final int SNACK_BAR_WHITE_COLOR = 0xFFF1F1F1;
private static final AtomicBoolean lithoSnackBarLoaded = new AtomicBoolean(false);
private static int blackColor = 0;
private static int whiteColor = 0;
public static boolean hideSnackBar() {
return HIDE_SNACK_BAR;
}
public static void hideLithoSnackBar(FrameLayout frameLayout) {
if (HIDE_SERVER_SIDE_SNACK_BAR) {
Utils.hideViewByLayoutParams(frameLayout);
}
}
public static void setLithoSnackBarBackground(View view) {
if (CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND) {
int snackBarRoundedCornersBackgroundIdentifier =
ResourceUtils.getDrawableIdentifier("snackbar_rounded_corners_background");
Context mContext = invertSnackBarTheme(view.getContext());
Drawable snackBarRoundedCornersBackground = mContext.getDrawable(snackBarRoundedCornersBackgroundIdentifier);
if (snackBarRoundedCornersBackground != null) {
view.setBackground(snackBarRoundedCornersBackground);
}
}
}
public static void setLithoSnackBarBackgroundColor(FrameLayout frameLayout, int color) {
if (CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND) {
return;
}
frameLayout.setBackgroundColor(color);
}
public static Context invertSnackBarTheme(Context mContext) {
if (INVERT_SERVER_SIDE_SNACK_BAR_THEME) {
String styleId = ThemeUtils.isDarkTheme()
? "Base.Theme.YouTube.Light"
: "Base.Theme.YouTube.Dark";
int styleIdentifier = ResourceUtils.getStyleIdentifier(styleId);
mContext = new ContextThemeWrapper(mContext, styleIdentifier);
}
return mContext;
}
public static Enum<?> invertSnackBarTheme(Enum<?> appTheme, Enum<?> darkTheme) {
if (INVERT_SNACK_BAR_THEME) {
return appTheme == darkTheme
? null
: darkTheme;
}
return appTheme;
}
public static void lithoSnackBarLoaded() {
lithoSnackBarLoaded.compareAndSet(false, true);
}
public static int getLithoColor(int originalValue) {
if (CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND &&
lithoSnackBarLoaded.compareAndSet(true, false)) {
if (originalValue == SNACK_BAR_BLACK_COLOR) {
return INVERT_SERVER_SIDE_SNACK_BAR_THEME
? getWhiteColor()
: getBlackColor();
} else if (originalValue == SNACK_BAR_WHITE_COLOR) {
return INVERT_SERVER_SIDE_SNACK_BAR_THEME
? getBlackColor()
: getWhiteColor();
}
}
return originalValue;
}
private static int getBlackColor() {
if (blackColor == 0) blackColor = ResourceUtils.getColor("revanced_snack_bar_color_dark");
return blackColor;
}
private static int getWhiteColor() {
if (whiteColor == 0) whiteColor = ResourceUtils.getColor("revanced_snack_bar_color_light");
return whiteColor;
}
}

View File

@ -0,0 +1,62 @@
package app.revanced.extension.youtube.patches.spans;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import app.revanced.extension.shared.patches.spans.Filter;
import app.revanced.extension.shared.patches.spans.SpanType;
import app.revanced.extension.shared.patches.spans.StringFilterGroup;
import app.revanced.extension.shared.utils.ResourceUtils;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.utils.ThemeUtils;
@SuppressWarnings({"unused", "FieldCanBeLocal"})
public final class SnackBarFilter extends Filter {
private static final boolean HIDE_SNACK_BAR =
Settings.HIDE_SNACK_BAR.get() || Settings.HIDE_SERVER_SIDE_SNACK_BAR.get();
private static final boolean CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND =
!HIDE_SNACK_BAR && Settings.CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND.get();
private static final boolean INVERT_SNACK_BAR_THEME =
!HIDE_SNACK_BAR && Settings.INVERT_SNACK_BAR_THEME.get();
private final ForegroundColorSpan foregroundColorSpanBlack =
new ForegroundColorSpan(ResourceUtils.getColor("yt_black1"));
private final ForegroundColorSpan foregroundColorSpanWhite =
new ForegroundColorSpan(ResourceUtils.getColor("yt_white1"));
public SnackBarFilter() {
addCallbacks(
new StringFilterGroup(
Settings.CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND,
"snackbar.eml|"
)
);
}
private ForegroundColorSpan getForegroundColorSpan() {
if (INVERT_SNACK_BAR_THEME) {
return ThemeUtils.isDarkTheme()
? foregroundColorSpanWhite
: foregroundColorSpanBlack;
}
return ThemeUtils.isDarkTheme()
? foregroundColorSpanBlack
: foregroundColorSpanWhite;
}
@Override
public boolean skip(String conversionContext, SpannableString spannableString, Object span,
int start, int end, int flags, boolean isWord, SpanType spanType, StringFilterGroup matchedGroup) {
if (CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND && spanType == SpanType.FOREGROUND_COLOR) {
spannableString.setSpan(
getForegroundColorSpan(),
start,
end,
flags
);
return super.skip(conversionContext, spannableString, span, start, end, flags, isWord, spanType, matchedGroup);
}
return false;
}
}

View File

@ -93,7 +93,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_FEED_SURVEY = new BooleanSetting("revanced_hide_feed_survey", TRUE);
public static final BooleanSetting HIDE_TICKET_SHELF = new BooleanSetting("revanced_hide_ticket_shelf", TRUE);
// PreferenceScreen: Feed - Category bar
public static final BooleanSetting HIDE_CATEGORY_BAR_IN_FEED = new BooleanSetting("revanced_hide_category_bar_in_feed", FALSE, true);
public static final BooleanSetting HIDE_CATEGORY_BAR_IN_SEARCH = new BooleanSetting("revanced_hide_category_bar_in_search", FALSE, true);
@ -152,7 +151,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting ENABLE_GRADIENT_LOADING_SCREEN = new BooleanSetting("revanced_enable_gradient_loading_screen", FALSE, true);
public static final BooleanSetting HIDE_FLOATING_MICROPHONE = new BooleanSetting("revanced_hide_floating_microphone", TRUE, true);
public static final BooleanSetting HIDE_GRAY_SEPARATOR = new BooleanSetting("revanced_hide_gray_separator", TRUE);
public static final BooleanSetting HIDE_SNACK_BAR = new BooleanSetting("revanced_hide_snack_bar", FALSE);
public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG = new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE);
public static final EnumSetting<FormFactor> CHANGE_LAYOUT = new EnumSetting<>("revanced_change_layout", FormFactor.ORIGINAL, true);
@ -232,6 +230,12 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SETTINGS_MENU_POST_PURCHASE = new BooleanSetting("revanced_hide_settings_menu_post_purchase", FALSE, true);
public static final BooleanSetting HIDE_SETTINGS_MENU_THIRD_PARTY = new BooleanSetting("revanced_hide_settings_menu_third_party", FALSE, true);
// PreferenceScreen: General - Snack bar
public static final BooleanSetting HIDE_SNACK_BAR = new BooleanSetting("revanced_hide_snack_bar", FALSE, true);
public static final BooleanSetting HIDE_SERVER_SIDE_SNACK_BAR = new BooleanSetting("revanced_hide_server_side_snack_bar", FALSE, true);
public static final BooleanSetting CHANGE_SERVER_SIDE_SNACK_BAR_BACKGROUND = new BooleanSetting("revanced_change_server_side_snack_bar_background", FALSE, true, "revanced_change_server_side_snack_bar_background_user_dialog_message");
public static final BooleanSetting INVERT_SNACK_BAR_THEME = new BooleanSetting("revanced_invert_snack_bar_theme", FALSE, true);
// PreferenceScreen: General - Toolbar
public static final BooleanSetting CHANGE_YOUTUBE_HEADER = new BooleanSetting("revanced_change_youtube_header", TRUE, true);
public static final BooleanSetting ENABLE_WIDE_SEARCH_BAR = new BooleanSetting("revanced_enable_wide_search_bar", FALSE, true);

View File

@ -30,13 +30,15 @@ val drawableColorHookPatch = bytecodePatch(
}
internal fun addDrawableColorHook(
methodDescriptor: String
methodDescriptor: String,
highPriority: Boolean = false
) {
insertMethod.addInstructions(
insertIndex + offset, """
invoke-static {v$insertRegister}, $methodDescriptor
move-result v$insertRegister
"""
if (highPriority) insertIndex else insertIndex + offset,
"""
invoke-static {v$insertRegister}, $methodDescriptor
move-result v$insertRegister
"""
)
offset += 2
}

View File

@ -69,16 +69,6 @@ internal val appBlockingCheckResultToStringFingerprint = legacyFingerprint(
strings = listOf("AppBlockingCheckResult{intent=")
)
internal val bottomUiContainerFingerprint = legacyFingerprint(
name = "bottomUiContainerFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L", "L"),
customFingerprint = { method, _ ->
method.definingClass.endsWith("/BottomUiContainer;")
}
)
internal val floatingMicrophoneFingerprint = legacyFingerprint(
name = "floatingMicrophoneFingerprint",
returnType = "V",

View File

@ -2,12 +2,10 @@ package app.revanced.patches.youtube.general.components
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.litho.addLithoFilter
import app.revanced.patches.shared.litho.lithoFilterPatch
import app.revanced.patches.shared.settingmenu.settingsMenuPatch
@ -215,21 +213,6 @@ val layoutComponentsPatch = bytecodePatch(
// endregion
// region patch for hide snack bar
bottomUiContainerFingerprint.methodOrThrow().apply {
addInstructionsWithLabels(
0, """
invoke-static {}, $GENERAL_CLASS_DESCRIPTOR->hideSnackBar()Z
move-result v0
if-eqz v0, :show
return-void
""", ExternalLabel("show", getInstruction(0))
)
}
// endregion
// region patch for hide tooltip content
tooltipContentFullscreenFingerprint.methodOrThrow().apply {

View File

@ -0,0 +1,68 @@
package app.revanced.patches.youtube.general.snackbar
import app.revanced.patches.youtube.utils.resourceid.insetElementsWrapper
import app.revanced.util.fingerprint.legacyFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversed
import app.revanced.util.or
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val BOTTOM_UI_CONTAINER_CLASS_DESCRIPTOR =
"Lcom/google/android/apps/youtube/app/common/ui/bottomui/BottomUiContainer;"
internal val bottomUiContainerFingerprint = legacyFingerprint(
name = "bottomUiContainerFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L", "L"),
customFingerprint = { _, classDef ->
classDef.type == BOTTOM_UI_CONTAINER_CLASS_DESCRIPTOR
}
)
internal val bottomUiContainerPreFingerprint = legacyFingerprint(
name = "bottomUiContainerPreFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf("L", "L", "L"),
opcodes = listOf(
Opcode.IF_NEZ,
Opcode.INVOKE_VIRTUAL,
Opcode.RETURN_VOID
),
customFingerprint = { _, classDef ->
classDef.type == BOTTOM_UI_CONTAINER_CLASS_DESCRIPTOR
}
)
internal val bottomUiContainerThemeFingerprint = legacyFingerprint(
name = "bottomUiContainerThemeFingerprint",
returnType = "V",
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
parameters = listOf(BOTTOM_UI_CONTAINER_CLASS_DESCRIPTOR),
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.SGET_OBJECT,
Opcode.IF_NE,
Opcode.CONST,
),
)
internal val lithoSnackBarFingerprint = legacyFingerprint(
name = "lithoSnackBarFingerprint",
returnType = "Landroid/view/View;",
literals = listOf(insetElementsWrapper),
customFingerprint = { method, _ ->
indexOfBackGroundColor(method) >= 0
}
)
internal fun indexOfBackGroundColor(method: Method) =
method.indexOfFirstInstructionReversed {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.name == "setBackgroundColor"
}

View File

@ -0,0 +1,340 @@
package app.revanced.patches.youtube.general.snackbar
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.shared.drawable.addDrawableColorHook
import app.revanced.patches.shared.drawable.drawableColorHookPatch
import app.revanced.patches.shared.spans.addSpanFilter
import app.revanced.patches.shared.spans.inclusiveSpanPatch
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.extension.Constants.GENERAL_PATH
import app.revanced.patches.youtube.utils.extension.Constants.SPANS_PATH
import app.revanced.patches.youtube.utils.patch.PatchList.SNACK_BAR_COMPONENTS
import app.revanced.patches.youtube.utils.resourceid.sharedResourceIdPatch
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
import app.revanced.patches.youtube.utils.settings.settingsPatch
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.findMethodOrThrow
import app.revanced.util.fingerprint.matchOrThrow
import app.revanced.util.fingerprint.methodOrThrow
import app.revanced.util.getNode
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.valueOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import org.w3c.dom.Element
private const val EXTENSION_CLASS_DESCRIPTOR =
"$GENERAL_PATH/SnackBarPatch;"
private const val FILTER_CLASS_DESCRIPTOR =
"$SPANS_PATH/SnackBarFilter;"
private val snackBarComponentsBytecodePatch = bytecodePatch(
description = "snackBarComponentsBytecodePatch"
) {
dependsOn(
settingsPatch,
sharedResourceIdPatch,
drawableColorHookPatch,
inclusiveSpanPatch,
)
execute {
bottomUiContainerFingerprint.methodOrThrow().apply {
addInstructionsWithLabels(
0, """
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->hideSnackBar()Z
move-result v0
if-eqz v0, :show
return-void
""", ExternalLabel("show", getInstruction(0))
)
}
bottomUiContainerPreFingerprint.matchOrThrow().let {
it.method.apply {
val insertIndex = it.patternMatch!!.startIndex + 1
addInstruction(
insertIndex,
"invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->lithoSnackBarLoaded()V"
)
}
}
bottomUiContainerThemeFingerprint.matchOrThrow().let {
it.method.apply {
val startIndex = it.patternMatch!!.startIndex
val appThemeIndex = startIndex + 1
val darkThemeIndex = startIndex + 2
val insertIndex = startIndex + 3
val appThemeRegister =
getInstruction<OneRegisterInstruction>(appThemeIndex).registerA
val darkThemeRegister =
getInstruction<OneRegisterInstruction>(darkThemeIndex).registerA
addInstructions(
insertIndex, """
invoke-static {v$appThemeRegister, v$darkThemeRegister}, $EXTENSION_CLASS_DESCRIPTOR->invertSnackBarTheme(Ljava/lang/Enum;Ljava/lang/Enum;)Ljava/lang/Enum;
move-result-object v$appThemeRegister
"""
)
}
}
fun MutableMethod.setBackground(index: Int, register: Int) =
addInstruction(
index,
"invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->setLithoSnackBarBackground(Landroid/view/View;)V"
)
lithoSnackBarFingerprint.methodOrThrow().apply {
val backGroundColorIndex = indexOfBackGroundColor(this)
val viewRegister =
getInstruction<FiveRegisterInstruction>(backGroundColorIndex).registerC
val colorRegister =
getInstruction<FiveRegisterInstruction>(backGroundColorIndex).registerD
replaceInstruction(
backGroundColorIndex,
"invoke-static {v$viewRegister, v$colorRegister}, $EXTENSION_CLASS_DESCRIPTOR->" +
"setLithoSnackBarBackgroundColor(Landroid/widget/FrameLayout;I)V"
)
setBackground(backGroundColorIndex + 2, viewRegister)
implementation!!.instructions
.withIndex()
.filter { (_, instruction) ->
instruction.opcode == Opcode.CHECK_CAST &&
(instruction as? ReferenceInstruction)?.reference?.toString() == "Landroid/widget/FrameLayout;"
}
.map { (index, _) -> index }
.reversed()
.forEach { index ->
val register =
getInstruction<OneRegisterInstruction>(index).registerA
setBackground(index + 1, register)
}
findMethodOrThrow(definingClass).apply {
val contextIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Landroid/content/Context;"
}
val contextRegister =
getInstruction<TwoRegisterInstruction>(contextIndex).registerA
addInstructions(
contextIndex, """
invoke-static {v$contextRegister}, $EXTENSION_CLASS_DESCRIPTOR->invertSnackBarTheme(Landroid/content/Context;)Landroid/content/Context;
move-result-object v$contextRegister
"""
)
val viewIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.type == "Landroid/widget/FrameLayout;"
}
val viewRegister =
getInstruction<TwoRegisterInstruction>(viewIndex).registerA
addInstructions(
viewIndex,
"invoke-static {v$viewRegister}, $EXTENSION_CLASS_DESCRIPTOR->hideLithoSnackBar(Landroid/widget/FrameLayout;)V"
)
}
}
addDrawableColorHook("$EXTENSION_CLASS_DESCRIPTOR->getLithoColor(I)I", true)
addSpanFilter(FILTER_CLASS_DESCRIPTOR)
}
}
@Suppress("unused")
val snackBarComponentsPatch = resourcePatch(
SNACK_BAR_COMPONENTS.title,
SNACK_BAR_COMPONENTS.summary,
) {
compatibleWith(COMPATIBLE_PACKAGE)
dependsOn(
settingsPatch,
snackBarComponentsBytecodePatch,
)
val catppuccinMochaColor = "#FF181825"
val catppuccinLatteColor = "#FFE6E9EF"
val availableDarkTheme = mapOf(
"Amoled Black" to "@android:color/black",
"Catppuccin (Mocha)" to "#FF181825",
"Dark Pink" to "#FF290025",
"Dark Blue" to "#FF001029",
"Dark Green" to "#FF002905",
"Dark Yellow" to "#FF282900",
"Dark Orange" to "#FF291800",
"Dark Red" to "#FF290000",
)
val availableLightTheme = mapOf(
"White" to "@android:color/white",
"Catppuccin (Latte)" to "#FFE6E9EF",
"Light Pink" to "#FFFCCFF3",
"Light Blue" to "#FFD1E0FF",
"Light Green" to "#FFCCFFCC",
"Light Yellow" to "#FFFDFFCC",
"Light Orange" to "#FFFFE6CC",
"Light Red" to "#FFFFD6D6",
)
val cornerRadiusOption = stringOption(
key = "cornerRadius",
default = "8.0dip",
title = "Corner radius",
description = "Specify a corner radius for the snack bar.",
required = true,
)
val darkThemeBackgroundColor = stringOption(
key = "darkThemeBackgroundColor",
default = catppuccinMochaColor,
values = availableDarkTheme,
title = "Dark theme background color",
description = "Specify a background color for the snack bar. You can specify hex color (#AARRGGBB) or color resource reference.",
required = true,
)
val lightThemeBackgroundColor = stringOption(
key = "lightThemeBackgroundColor",
default = catppuccinLatteColor,
values = availableLightTheme,
title = "Light theme background color",
description = "Specify a background color for the snack bar. You can specify hex color (#AARRGGBB) or color resource reference.",
required = true,
)
val strokeColorOption = stringOption(
key = "strokeColor",
default = "",
values = mapOf(
"None" to "",
"Blue" to "?attr/ytThemedBlue",
"Chip" to "?attr/ytChipBackground"
),
title = "Stroke color",
description = "Specify a stroke color for the snack bar. You can specify hex color.",
required = true,
)
execute {
// Check patch options first.
val cornerRadius = cornerRadiusOption
.valueOrThrow()
val darkThemeColor = darkThemeBackgroundColor
.valueOrThrow()
val lightThemeColor = lightThemeBackgroundColor
.valueOrThrow()
val strokeColor = strokeColorOption
.valueOrThrow()
val snackBarColorAttr = "snackBarColor"
val snackBarColorAttrReference = "?attr/$snackBarColorAttr"
val snackBarColorDark = "revanced_snack_bar_color_dark"
val snackBarColorDarkReference = "@color/$snackBarColorDark"
val snackBarColorLight = "revanced_snack_bar_color_light"
val snackBarColorLightReference = "@color/$snackBarColorLight"
document("res/values/colors.xml").use { document ->
mapOf(
snackBarColorDark to darkThemeColor,
snackBarColorLight to lightThemeColor,
).forEach { (k, v) ->
val colorElement = document.createElement("color")
colorElement.setAttribute("name", k)
colorElement.textContent = v
document.getElementsByTagName("resources").item(0)
.appendChild(colorElement)
}
}
document("res/values/attrs.xml").use { document ->
(document.getElementsByTagName("resources").item(0) as Element).appendChild(
document.createElement("attr").apply {
setAttribute("format", "reference|color")
setAttribute("name", snackBarColorAttr)
}
)
}
document("res/values/styles.xml").use { document ->
mapOf(
"Base.Theme.YouTube.Dark" to snackBarColorLightReference,
"Base.Theme.YouTube.Light" to snackBarColorDarkReference,
).forEach { (styleName, colorName) ->
val snackBarColorNode = document.createElement("item")
snackBarColorNode.setAttribute("name", snackBarColorAttr)
snackBarColorNode.appendChild(document.createTextNode(colorName))
document.childNodes.findElementByAttributeValueOrThrow(
"name",
styleName,
).appendChild(snackBarColorNode)
}
}
document("res/drawable/snackbar_rounded_corners_background.xml").use { document ->
document.getNode("corners").apply {
arrayOf(
"android:bottomLeftRadius",
"android:bottomRightRadius",
"android:topLeftRadius",
"android:topRightRadius",
).forEach {
attributes.getNamedItem(it).nodeValue = cornerRadius
}
}
document.getNode("solid").apply {
attributes.getNamedItem("android:color").nodeValue = snackBarColorAttrReference
}
if (!strokeColor.isEmpty()) {
(document.getElementsByTagName("shape").item(0) as Element).appendChild(
document.createElement("stroke").apply {
setAttribute("android:width", "1.0dip")
setAttribute("android:color", strokeColor)
}
)
}
}
// region add settings
addPreference(
arrayOf(
"PREFERENCE_SCREEN: GENERAL",
"SETTINGS: SNACK_BAR_COMPONENTS"
),
SNACK_BAR_COMPONENTS
)
// endregion
}
}

View File

@ -217,6 +217,10 @@ internal enum class PatchList(
"Shorts components",
"Adds options to hide or change components related to YouTube Shorts."
),
SNACK_BAR_COMPONENTS(
"Snack bar components",
"Adds options to hide or change components related to the snack bar."
),
SPONSORBLOCK(
"SponsorBlock",
"Adds options to enable and configure SponsorBlock, which can skip undesired video segments, such as sponsored content."

View File

@ -121,6 +121,8 @@ var insetOverlayViewLayout = -1L
private set
var interstitialsContainer = -1L
private set
var insetElementsWrapper = -1L
private set
var menuItemView = -1L
private set
var metaPanel = -1L
@ -461,6 +463,10 @@ internal val sharedResourceIdPatch = resourcePatch(
ID,
"interstitials_container"
]
insetElementsWrapper = resourceMappings[
LAYOUT,
"inset_elements_wrapper"
]
menuItemView = resourceMappings[
ID,
"menu_item_view"

View File

@ -425,9 +425,6 @@ Limitation: Back button on the toolbar may not work."</string>
<string name="revanced_hide_gray_separator_title">Hide gray separators</string>
<string name="revanced_hide_gray_separator_summary_on">Gray separators are hidden.</string>
<string name="revanced_hide_gray_separator_summary_off">Gray separators are shown.</string>
<string name="revanced_hide_snack_bar_title">Hide snack bar</string>
<string name="revanced_hide_snack_bar_summary_on">Snack bar is hidden.</string>
<string name="revanced_hide_snack_bar_summary_off">Snack bar is shown.</string>
<string name="revanced_remove_viewer_discretion_dialog_title">Remove viewer discretion dialog</string>
<string name="revanced_remove_viewer_discretion_dialog_summary">"Removes the viewer discretion dialog.
This does not bypass the age restriction. It just accepts it automatically."</string>
@ -677,6 +674,28 @@ If this setting do not take effect, try switching to Incognito mode."</string>
<string name="revanced_hide_settings_menu_about_summary_on">About menu is hidden.</string>
<string name="revanced_hide_settings_menu_about_summary_off">About menu is shown.</string>
<!-- PreferenceScreen: General, PreferenceCategory: General, PreferenceScreen: Snack bar -->
<string name="revanced_preference_screen_snack_bar_title">Snack bar</string>
<string name="revanced_preference_screen_snack_bar_summary">Hide or change components related to snack bar.</string>
<string name="revanced_hide_snack_bar_title">Hide snack bar</string>
<string name="revanced_hide_snack_bar_summary_on">Snack bar is hidden.</string>
<string name="revanced_hide_snack_bar_summary_off">Snack bar is shown.</string>
<string name="revanced_hide_server_side_snack_bar_title">Hide server-side snack bar</string>
<string name="revanced_hide_server_side_snack_bar_summary_on">Server-side snack bar is hidden.</string>
<string name="revanced_hide_server_side_snack_bar_summary_off">Server-side snack bar is shown.</string>
<string name="revanced_invert_snack_bar_theme_title">Invert snack bar theme</string>
<string name="revanced_invert_snack_bar_theme_summary_on">Theme of the snack bar is inverted.</string>
<string name="revanced_invert_snack_bar_theme_summary_off">Theme of the snack bar is not inverted.</string>
<string name="revanced_change_server_side_snack_bar_background_title">Change server-side snack bar background</string>
<string name="revanced_change_server_side_snack_bar_background_summary_on">Background color of the server-side snackbar has changed.</string>
<string name="revanced_change_server_side_snack_bar_background_summary_off">Background color of the server-side snackbar has not changed.</string>
<string name="revanced_change_server_side_snack_bar_background_user_dialog_message">"Some snack bars use a theme defined on the server side, not the app theme.
Change the background color of these snack bars.
If there are server-side changes, the background color of the snack bar may not change."</string>
<!-- PreferenceScreen: General, PreferenceCategory: General, PreferenceScreen: Toolbar -->
<string name="revanced_preference_screen_toolbar_title">Toolbar</string>
<string name="revanced_preference_screen_toolbar_summary">Hide or change components located on the toolbar, such as the search bar, toolbar buttons, and header.</string>

View File

@ -244,6 +244,15 @@
<SwitchPreference android:title="@string/revanced_hide_settings_menu_about_title" android:key="revanced_hide_settings_menu_about" android:summaryOn="@string/revanced_hide_settings_menu_about_summary_on" android:summaryOff="@string/revanced_hide_settings_menu_about_summary_off" />
</PreferenceScreen>SETTINGS: HIDE_LAYOUT_COMPONENTS -->
<!-- SETTINGS: SNACK_BAR_COMPONENTS
<PreferenceScreen android:title="@string/revanced_preference_screen_snack_bar_title" android:key="revanced_preference_screen_snack_bar" android:summary="@string/revanced_preference_screen_snack_bar_summary">
<SwitchPreference android:title="@string/revanced_hide_snack_bar_title" android:key="revanced_hide_snack_bar" android:summaryOn="@string/revanced_hide_snack_bar_summary_on" android:summaryOff="@string/revanced_hide_snack_bar_summary_off" />
<SwitchPreference android:title="@string/revanced_hide_server_side_snack_bar_title" android:key="revanced_hide_server_side_snack_bar" android:summaryOn="@string/revanced_hide_server_side_snack_bar_summary_on" android:summaryOff="@string/revanced_hide_server_side_snack_bar_summary_off" />
<SwitchPreference android:title="@string/revanced_invert_snack_bar_theme_title" android:key="revanced_invert_snack_bar_theme" android:summaryOn="@string/revanced_invert_snack_bar_theme_summary_on" android:summaryOff="@string/revanced_invert_snack_bar_theme_summary_off" />
<PreferenceCategory android:title="@string/revanced_preference_category_experimental_flag" android:layout="@layout/revanced_settings_preferences_category"/>
<SwitchPreference android:title="@string/revanced_change_server_side_snack_bar_background_title" android:key="revanced_change_server_side_snack_bar_background" android:summaryOn="@string/revanced_change_server_side_snack_bar_background_summary_on" android:summaryOff="@string/revanced_change_server_side_snack_bar_background_summary_off" />
</PreferenceScreen>SETTINGS: SNACK_BAR_COMPONENTS -->
<!-- SETTINGS: TOOLBAR_COMPONENTS
<PreferenceScreen android:title="@string/revanced_preference_screen_toolbar_title" android:key="revanced_preference_screen_toolbar" android:summary="@string/revanced_preference_screen_toolbar_summary">
<SwitchPreference android:title="@string/revanced_change_youtube_header_title" android:key="revanced_change_youtube_header" android:summaryOn="@string/revanced_change_youtube_header_summary_on" android:summaryOff="@string/revanced_change_youtube_header_summary_off" />
@ -289,8 +298,7 @@
<!-- SETTINGS: HIDE_LAYOUT_COMPONENTS
<SwitchPreference android:title="@string/revanced_hide_floating_microphone_title" android:key="revanced_hide_floating_microphone" android:summaryOn="@string/revanced_hide_floating_microphone_summary_on" android:summaryOff="@string/revanced_hide_floating_microphone_summary_off" />
<SwitchPreference android:title="@string/revanced_hide_gray_separator_title" android:key="revanced_hide_gray_separator" android:summaryOn="@string/revanced_hide_gray_separator_summary_on" android:summaryOff="@string/revanced_hide_gray_separator_summary_off" />
<SwitchPreference android:title="@string/revanced_hide_snack_bar_title" android:key="revanced_hide_snack_bar" android:summaryOn="@string/revanced_hide_snack_bar_summary_on" android:summaryOff="@string/revanced_hide_snack_bar_summary_off" />SETTINGS: HIDE_LAYOUT_COMPONENTS -->
<SwitchPreference android:title="@string/revanced_hide_gray_separator_title" android:key="revanced_hide_gray_separator" android:summaryOn="@string/revanced_hide_gray_separator_summary_on" android:summaryOff="@string/revanced_hide_gray_separator_summary_off" />SETTINGS: HIDE_LAYOUT_COMPONENTS -->
<!-- SETTINGS: MINIPLAYER_TYPE_19_14
<ListPreference android:entries="@array/revanced_miniplayer_type_19_14_entries" android:title="@string/revanced_miniplayer_type_title" android:key="revanced_miniplayer_type" android:entryValues="@array/revanced_miniplayer_type_19_14_entry_values" />SETTINGS: MINIPLAYER_TYPE_19_14 -->
@ -914,6 +922,7 @@
<Preference android:title="Miniplayer" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
<Preference android:title="Navigation bar components" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
<Preference android:title="Remove viewer discretion dialog" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
<Preference android:title="Snack bar components" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
<Preference android:title="Spoof app version" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
<Preference android:title="Toolbar components" android:summary="@string/revanced_patches_excluded" android:selectable="false"/>
</PreferenceCategory>