mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-04 16:44:29 +02:00
feat(YouTube/Hide layout components): filter home/search results by keywords
This commit is contained in:
parent
017ef705f8
commit
3bb394643e
@ -21,13 +21,16 @@ object LayoutComponentsPatch : BaseBytecodePatch(
|
|||||||
"$COMPONENTS_PATH/ChannelBarFilter;"
|
"$COMPONENTS_PATH/ChannelBarFilter;"
|
||||||
private const val CUSTOM_FILTER_CLASS_DESCRIPTOR =
|
private const val CUSTOM_FILTER_CLASS_DESCRIPTOR =
|
||||||
"$COMPONENTS_PATH/CustomFilter;"
|
"$COMPONENTS_PATH/CustomFilter;"
|
||||||
private const val FILTER_CLASS_DESCRIPTOR =
|
private const val LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR =
|
||||||
"$COMPONENTS_PATH/LayoutComponentsFilter;"
|
"$COMPONENTS_PATH/LayoutComponentsFilter;"
|
||||||
|
private const val KEYWORD_FILTER_CLASS_NAME =
|
||||||
|
"$COMPONENTS_PATH/KeywordContentFilter;"
|
||||||
|
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
LithoFilterPatch.addFilter(CHANNEL_BAR_FILTER_CLASS_DESCRIPTOR)
|
LithoFilterPatch.addFilter(CHANNEL_BAR_FILTER_CLASS_DESCRIPTOR)
|
||||||
LithoFilterPatch.addFilter(CUSTOM_FILTER_CLASS_DESCRIPTOR)
|
LithoFilterPatch.addFilter(CUSTOM_FILTER_CLASS_DESCRIPTOR)
|
||||||
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)
|
LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR)
|
||||||
|
LithoFilterPatch.addFilter(KEYWORD_FILTER_CLASS_NAME)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add settings
|
* Add settings
|
||||||
|
@ -1,26 +1,16 @@
|
|||||||
package app.revanced.patches.youtube.navigation.navigationbuttons
|
package app.revanced.patches.youtube.navigation.navigationbuttons
|
||||||
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
|
||||||
import app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints.AutoMotiveFingerprint
|
import app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints.AutoMotiveFingerprint
|
||||||
import app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints.PivotBarButtonViewFingerprint
|
|
||||||
import app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints.PivotBarEnumFingerprint
|
|
||||||
import app.revanced.patches.youtube.utils.fingerprints.PivotBarCreateButtonViewFingerprint
|
|
||||||
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
|
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
|
||||||
import app.revanced.patches.youtube.utils.integrations.Constants.NAVIGATION_CLASS_DESCRIPTOR
|
import app.revanced.patches.youtube.utils.integrations.Constants.NAVIGATION_CLASS_DESCRIPTOR
|
||||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
|
import app.revanced.patches.youtube.utils.navigation.NavigationBarHookPatch
|
||||||
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ImageOnlyTab
|
|
||||||
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
import app.revanced.patches.youtube.utils.settings.SettingsPatch
|
||||||
import app.revanced.util.getStringInstructionIndex
|
import app.revanced.util.getStringInstructionIndex
|
||||||
import app.revanced.util.getTargetIndex
|
|
||||||
import app.revanced.util.getWideLiteralInstructionIndex
|
|
||||||
import app.revanced.util.patch.BaseBytecodePatch
|
import app.revanced.util.patch.BaseBytecodePatch
|
||||||
import app.revanced.util.resultOrThrow
|
import app.revanced.util.resultOrThrow
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode.MOVE_RESULT_OBJECT
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@ -29,60 +19,13 @@ object NavigationButtonsPatch : BaseBytecodePatch(
|
|||||||
description = "Adds options to hide and change navigation buttons (such as the Shorts button).",
|
description = "Adds options to hide and change navigation buttons (such as the Shorts button).",
|
||||||
dependencies = setOf(
|
dependencies = setOf(
|
||||||
SettingsPatch::class,
|
SettingsPatch::class,
|
||||||
SharedResourceIdPatch::class
|
NavigationBarHookPatch::class
|
||||||
),
|
),
|
||||||
compatiblePackages = COMPATIBLE_PACKAGE,
|
compatiblePackages = COMPATIBLE_PACKAGE,
|
||||||
fingerprints = setOf(
|
fingerprints = setOf(AutoMotiveFingerprint)
|
||||||
AutoMotiveFingerprint,
|
|
||||||
PivotBarCreateButtonViewFingerprint
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
|
|
||||||
PivotBarCreateButtonViewFingerprint.resultOrThrow().let { parentResult ->
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Home, Shorts, Subscriptions Button
|
|
||||||
*/
|
|
||||||
with(
|
|
||||||
arrayOf(
|
|
||||||
PivotBarEnumFingerprint,
|
|
||||||
PivotBarButtonViewFingerprint
|
|
||||||
).onEach {
|
|
||||||
it.resolve(
|
|
||||||
context,
|
|
||||||
parentResult.mutableMethod,
|
|
||||||
parentResult.mutableClass
|
|
||||||
)
|
|
||||||
}.map {
|
|
||||||
it.resultOrThrow().scanResult.patternScanResult!!
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
val enumScanResult = this[0]
|
|
||||||
val buttonViewResult = this[1]
|
|
||||||
|
|
||||||
val enumHookInsertIndex = enumScanResult.startIndex + 2
|
|
||||||
val buttonHookInsertIndex = buttonViewResult.endIndex
|
|
||||||
|
|
||||||
mapOf(
|
|
||||||
BUTTON_HOOK to buttonHookInsertIndex,
|
|
||||||
ENUM_HOOK to enumHookInsertIndex
|
|
||||||
).forEach { (hook, insertIndex) ->
|
|
||||||
parentResult.mutableMethod.injectHook(hook, insertIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create Button
|
|
||||||
*/
|
|
||||||
parentResult.mutableMethod.apply {
|
|
||||||
val constIndex = getWideLiteralInstructionIndex(ImageOnlyTab)
|
|
||||||
val insertIndex = getTargetIndex(constIndex, Opcode.INVOKE_VIRTUAL) + 2
|
|
||||||
injectHook(CREATE_BUTTON_HOOK, insertIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switch create button with notifications button
|
* Switch create button with notifications button
|
||||||
*/
|
*/
|
||||||
@ -93,13 +36,17 @@ object NavigationButtonsPatch : BaseBytecodePatch(
|
|||||||
|
|
||||||
addInstructions(
|
addInstructions(
|
||||||
insertIndex, """
|
insertIndex, """
|
||||||
invoke-static {v$register}, $NAVIGATION_CLASS_DESCRIPTOR->switchCreateNotification(Z)Z
|
invoke-static {v$register}, $NAVIGATION_CLASS_DESCRIPTOR->switchCreateWithNotificationButton(Z)Z
|
||||||
move-result v$register
|
move-result v$register
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hook navigation button created, in order to hide them.
|
||||||
|
NavigationBarHookPatch.hookNavigationButtonCreated(NAVIGATION_CLASS_DESCRIPTOR)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add settings
|
* Add settings
|
||||||
*/
|
*/
|
||||||
@ -113,40 +60,4 @@ object NavigationButtonsPatch : BaseBytecodePatch(
|
|||||||
SettingsPatch.updatePatchStatus("Hide navigation buttons")
|
SettingsPatch.updatePatchStatus("Hide navigation buttons")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val REGISTER_TEMPLATE_REPLACEMENT: String = "REGISTER_INDEX"
|
|
||||||
|
|
||||||
private const val ENUM_HOOK =
|
|
||||||
"sput-object v$REGISTER_TEMPLATE_REPLACEMENT, $NAVIGATION_CLASS_DESCRIPTOR" +
|
|
||||||
"->" +
|
|
||||||
"lastPivotTab:Ljava/lang/Enum;"
|
|
||||||
|
|
||||||
private const val BUTTON_HOOK =
|
|
||||||
"invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, $NAVIGATION_CLASS_DESCRIPTOR" +
|
|
||||||
"->" +
|
|
||||||
"hideNavigationButton(Landroid/view/View;)V"
|
|
||||||
|
|
||||||
private const val CREATE_BUTTON_HOOK =
|
|
||||||
"invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, $NAVIGATION_CLASS_DESCRIPTOR" +
|
|
||||||
"->" +
|
|
||||||
"hideCreateButton(Landroid/view/View;)V"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injects an instruction into insertIndex of the hook.
|
|
||||||
* @param hook The hook to insert.
|
|
||||||
* @param insertIndex The index to insert the instruction at.
|
|
||||||
* [MOVE_RESULT_OBJECT] has to be the previous instruction before [insertIndex].
|
|
||||||
*/
|
|
||||||
private fun MutableMethod.injectHook(hook: String, insertIndex: Int) {
|
|
||||||
val injectTarget = this
|
|
||||||
|
|
||||||
// Register to pass to the hook
|
|
||||||
val registerIndex = insertIndex - 1 // MOVE_RESULT_OBJECT is always the previous instruction
|
|
||||||
val register = injectTarget.getInstruction<OneRegisterInstruction>(registerIndex).registerA
|
|
||||||
|
|
||||||
injectTarget.addInstruction(
|
|
||||||
insertIndex,
|
|
||||||
hook.replace("REGISTER_INDEX", register.toString()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,12 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
internal object PivotBarButtonViewFingerprint : MethodFingerprint(
|
|
||||||
opcodes = listOf(
|
|
||||||
Opcode.INVOKE_VIRTUAL_RANGE,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT, // target reference
|
|
||||||
null,
|
|
||||||
)
|
|
||||||
)
|
|
@ -1,15 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
|
|
||||||
internal object PivotBarEnumFingerprint : MethodFingerprint(
|
|
||||||
opcodes = listOf(
|
|
||||||
Opcode.INVOKE_STATIC,
|
|
||||||
Opcode.MOVE_RESULT_OBJECT,
|
|
||||||
Opcode.IF_NEZ, // target reference
|
|
||||||
Opcode.SGET_OBJECT,
|
|
||||||
Opcode.INVOKE_VIRTUAL,
|
|
||||||
Opcode.MOVE_RESULT,
|
|
||||||
)
|
|
||||||
)
|
|
@ -8,7 +8,7 @@ import app.revanced.patcher.patch.BytecodePatch
|
|||||||
import app.revanced.patches.youtube.shorts.components.fingerprints.BottomNavigationBarFingerprint
|
import app.revanced.patches.youtube.shorts.components.fingerprints.BottomNavigationBarFingerprint
|
||||||
import app.revanced.patches.youtube.shorts.components.fingerprints.RenderBottomNavigationBarFingerprint
|
import app.revanced.patches.youtube.shorts.components.fingerprints.RenderBottomNavigationBarFingerprint
|
||||||
import app.revanced.patches.youtube.shorts.components.fingerprints.SetPivotBarFingerprint
|
import app.revanced.patches.youtube.shorts.components.fingerprints.SetPivotBarFingerprint
|
||||||
import app.revanced.patches.youtube.utils.fingerprints.PivotBarCreateButtonViewFingerprint
|
import app.revanced.patches.youtube.utils.fingerprints.InitializeButtonsFingerprint
|
||||||
import app.revanced.patches.youtube.utils.integrations.Constants.SHORTS_CLASS_DESCRIPTOR
|
import app.revanced.patches.youtube.utils.integrations.Constants.SHORTS_CLASS_DESCRIPTOR
|
||||||
import app.revanced.util.getTargetIndexWithMethodReferenceName
|
import app.revanced.util.getTargetIndexWithMethodReferenceName
|
||||||
import app.revanced.util.getWalkerMethod
|
import app.revanced.util.getWalkerMethod
|
||||||
@ -18,13 +18,13 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
|||||||
object ShortsNavigationBarPatch : BytecodePatch(
|
object ShortsNavigationBarPatch : BytecodePatch(
|
||||||
setOf(
|
setOf(
|
||||||
BottomNavigationBarFingerprint,
|
BottomNavigationBarFingerprint,
|
||||||
PivotBarCreateButtonViewFingerprint,
|
InitializeButtonsFingerprint,
|
||||||
RenderBottomNavigationBarFingerprint
|
RenderBottomNavigationBarFingerprint
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
override fun execute(context: BytecodeContext) {
|
override fun execute(context: BytecodeContext) {
|
||||||
|
|
||||||
PivotBarCreateButtonViewFingerprint.resultOrThrow().let { parentResult ->
|
InitializeButtonsFingerprint.resultOrThrow().let { parentResult ->
|
||||||
SetPivotBarFingerprint.also { it.resolve(context, parentResult.classDef) }.resultOrThrow().let {
|
SetPivotBarFingerprint.also { it.resolve(context, parentResult.classDef) }.resultOrThrow().let {
|
||||||
it.mutableMethod.apply {
|
it.mutableMethod.apply {
|
||||||
val startIndex = it.scanResult.patternScanResult!!.startIndex
|
val startIndex = it.scanResult.patternScanResult!!.startIndex
|
||||||
|
@ -5,8 +5,9 @@ import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.Image
|
|||||||
import app.revanced.util.fingerprint.LiteralValueFingerprint
|
import app.revanced.util.fingerprint.LiteralValueFingerprint
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
internal object PivotBarCreateButtonViewFingerprint : LiteralValueFingerprint(
|
internal object InitializeButtonsFingerprint : LiteralValueFingerprint(
|
||||||
returnType = "V",
|
|
||||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "V",
|
||||||
|
parameters = emptyList(),
|
||||||
literalSupplier = { ImageOnlyTab }
|
literalSupplier = { ImageOnlyTab }
|
||||||
)
|
)
|
@ -1,12 +1,12 @@
|
|||||||
package app.revanced.patches.youtube.utils.flyoutpanel
|
package app.revanced.patches.youtube.utils.flyoutpanel
|
||||||
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
import app.revanced.patcher.data.BytecodeContext
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||||
import app.revanced.patcher.extensions.or
|
import app.revanced.patcher.extensions.or
|
||||||
import app.revanced.patcher.patch.BytecodePatch
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
import app.revanced.patcher.patch.PatchException
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
import app.revanced.patches.youtube.utils.flyoutpanel.fingerprints.PlaybackRateBottomSheetClassFingerprint
|
import app.revanced.patches.youtube.utils.flyoutpanel.fingerprints.PlaybackRateBottomSheetClassFingerprint
|
||||||
import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH
|
import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH
|
||||||
|
@ -0,0 +1,138 @@
|
|||||||
|
package app.revanced.patches.youtube.utils.navigation
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patches.youtube.utils.fingerprints.InitializeButtonsFingerprint
|
||||||
|
import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH
|
||||||
|
import app.revanced.patches.youtube.utils.navigation.fingerprints.ActionBarSearchResultsFingerprint
|
||||||
|
import app.revanced.patches.youtube.utils.navigation.fingerprints.NavigationEnumFingerprint
|
||||||
|
import app.revanced.patches.youtube.utils.navigation.fingerprints.PivotBarButtonsCreateDrawableViewFingerprint
|
||||||
|
import app.revanced.patches.youtube.utils.navigation.fingerprints.PivotBarButtonsCreateResourceViewFingerprint
|
||||||
|
import app.revanced.patches.youtube.utils.navigation.fingerprints.PivotBarConstructorFingerprint
|
||||||
|
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
|
||||||
|
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.getTargetIndexWithMethodReferenceName
|
||||||
|
import app.revanced.util.resultOrThrow
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
description = "Hooks the active navigation or search bar.",
|
||||||
|
dependencies = [
|
||||||
|
PlayerTypeHookPatch::class,
|
||||||
|
SharedResourceIdPatch::class
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
object NavigationBarHookPatch : BytecodePatch(
|
||||||
|
setOf(
|
||||||
|
ActionBarSearchResultsFingerprint,
|
||||||
|
NavigationEnumFingerprint,
|
||||||
|
PivotBarButtonsCreateDrawableViewFingerprint,
|
||||||
|
PivotBarButtonsCreateResourceViewFingerprint,
|
||||||
|
PivotBarConstructorFingerprint
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
internal const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||||
|
"$INTEGRATIONS_PATH/shared/NavigationBar;"
|
||||||
|
|
||||||
|
private const val INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR =
|
||||||
|
"$INTEGRATIONS_PATH/shared/NavigationBar\$NavigationButton;"
|
||||||
|
|
||||||
|
private lateinit var navigationTabCreatedCallback: MutableMethod
|
||||||
|
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
fun MutableMethod.addHook(hook: Hook, insertPredicate: Instruction.() -> Boolean) {
|
||||||
|
val filtered = getInstructions().filter(insertPredicate)
|
||||||
|
if (filtered.isEmpty()) throw PatchException("Could not find insert indexes")
|
||||||
|
filtered.forEach {
|
||||||
|
val insertIndex = it.location.index + 2
|
||||||
|
val register = getInstruction<OneRegisterInstruction>(insertIndex - 1).registerA
|
||||||
|
|
||||||
|
addInstruction(
|
||||||
|
insertIndex,
|
||||||
|
"invoke-static { v$register }, " +
|
||||||
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->${hook.methodName}(${hook.parameters})V",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeButtonsFingerprint.apply {
|
||||||
|
resolve(context, PivotBarConstructorFingerprint.resultOrThrow().classDef)
|
||||||
|
}.resultOrThrow().mutableMethod.apply {
|
||||||
|
// Hook the current navigation bar enum value. Note, the 'You' tab does not have an enum value.
|
||||||
|
val navigationEnumClassName = NavigationEnumFingerprint.resultOrThrow().mutableClass.type
|
||||||
|
addHook(Hook.SET_LAST_APP_NAVIGATION_ENUM) {
|
||||||
|
opcode == Opcode.INVOKE_STATIC &&
|
||||||
|
getReference<MethodReference>()?.definingClass == navigationEnumClassName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook the creation of navigation tab views.
|
||||||
|
val drawableTabMethod = PivotBarButtonsCreateDrawableViewFingerprint.resultOrThrow().mutableMethod
|
||||||
|
addHook(Hook.NAVIGATION_TAB_LOADED) predicate@{
|
||||||
|
MethodUtil.methodSignaturesMatch(
|
||||||
|
getReference<MethodReference>() ?: return@predicate false,
|
||||||
|
drawableTabMethod,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val imageResourceTabMethod = PivotBarButtonsCreateResourceViewFingerprint.resultOrThrow().method
|
||||||
|
addHook(Hook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{
|
||||||
|
MethodUtil.methodSignaturesMatch(
|
||||||
|
getReference<MethodReference>() ?: return@predicate false,
|
||||||
|
imageResourceTabMethod,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook the search bar.
|
||||||
|
|
||||||
|
// Two different layouts are used at the hooked code.
|
||||||
|
// Insert before the first ViewGroup method call after inflating,
|
||||||
|
// so this works regardless which layout is used.
|
||||||
|
ActionBarSearchResultsFingerprint.resultOrThrow().mutableMethod.apply {
|
||||||
|
val instructionIndex = getTargetIndexWithMethodReferenceName("setLayoutDirection")
|
||||||
|
val viewRegister = getInstruction<FiveRegisterInstruction>(instructionIndex).registerC
|
||||||
|
|
||||||
|
addInstruction(
|
||||||
|
instructionIndex,
|
||||||
|
"invoke-static { v$viewRegister }, " +
|
||||||
|
"$INTEGRATIONS_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
navigationTabCreatedCallback = context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)?.mutableClass?.methods?.first { method ->
|
||||||
|
method.name == "navigationTabCreatedCallback"
|
||||||
|
} ?: throw PatchException("Could not find navigationTabCreatedCallback method")
|
||||||
|
}
|
||||||
|
|
||||||
|
val hookNavigationButtonCreated: (String) -> Unit by lazy {
|
||||||
|
navigationTabCreatedCallback
|
||||||
|
{ integrationsClassDescriptor ->
|
||||||
|
navigationTabCreatedCallback.addInstruction(
|
||||||
|
0,
|
||||||
|
"invoke-static { p0, p1 }, " +
|
||||||
|
"$integrationsClassDescriptor->navigationTabCreated" +
|
||||||
|
"(${INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class Hook(val methodName: String, val parameters: String) {
|
||||||
|
SET_LAST_APP_NAVIGATION_ENUM("setLastAppNavigationEnum", "Ljava/lang/Enum;"),
|
||||||
|
NAVIGATION_TAB_LOADED("navigationTabLoaded", "Landroid/view/View;"),
|
||||||
|
NAVIGATION_IMAGE_RESOURCE_TAB_LOADED("navigationImageResourceTabLoaded", "Landroid/view/View;"),
|
||||||
|
SEARCH_BAR_RESULTS_VIEW_LOADED("searchBarResultsViewLoaded", "Landroid/view/View;"),
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package app.revanced.patches.youtube.utils.navigation.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ActionBarSearchResultsViewMic
|
||||||
|
import app.revanced.util.fingerprint.LiteralValueFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object ActionBarSearchResultsFingerprint : LiteralValueFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
returnType = "Landroid/view/View;",
|
||||||
|
parameters = listOf("Landroid/view/LayoutInflater;"),
|
||||||
|
literalSupplier = { ActionBarSearchResultsViewMic }
|
||||||
|
)
|
@ -0,0 +1,21 @@
|
|||||||
|
package app.revanced.patches.youtube.utils.navigation.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves to the Enum class that looks up ordinal -> instance.
|
||||||
|
*/
|
||||||
|
internal object NavigationEnumFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.STATIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
strings = listOf(
|
||||||
|
"PIVOT_HOME",
|
||||||
|
"TAB_SHORTS",
|
||||||
|
"CREATION_TAB_LARGE",
|
||||||
|
"PIVOT_SUBSCRIPTIONS",
|
||||||
|
"TAB_ACTIVITY",
|
||||||
|
"VIDEO_LIBRARY_WHITE",
|
||||||
|
"INCOGNITO_CIRCLE"
|
||||||
|
)
|
||||||
|
)
|
@ -0,0 +1,17 @@
|
|||||||
|
package app.revanced.patches.youtube.utils.navigation.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object PivotBarButtonsCreateDrawableViewFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
// Method has different number of parameters in some app targets.
|
||||||
|
// Parameters are checked in custom fingerprint.
|
||||||
|
returnType = "Landroid/view/View;",
|
||||||
|
customFingerprint = { methodDef, classDef ->
|
||||||
|
classDef.type == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" &&
|
||||||
|
// Only one method has a Drawable parameter.
|
||||||
|
methodDef.parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;"
|
||||||
|
}
|
||||||
|
)
|
@ -0,0 +1,14 @@
|
|||||||
|
package app.revanced.patches.youtube.utils.navigation.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object PivotBarButtonsCreateResourceViewFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
parameters = listOf("L", "Z", "I", "L"),
|
||||||
|
returnType = "Landroid/view/View;",
|
||||||
|
customFingerprint = { _, classDef ->
|
||||||
|
classDef.type == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;"
|
||||||
|
}
|
||||||
|
)
|
@ -0,0 +1,10 @@
|
|||||||
|
package app.revanced.patches.youtube.utils.navigation.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
|
||||||
|
internal object PivotBarConstructorFingerprint : MethodFingerprint(
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
strings = listOf("com.google.android.apps.youtube.app.endpoint.flags")
|
||||||
|
)
|
@ -18,6 +18,7 @@ import app.revanced.patches.shared.mapping.ResourceType.STYLE
|
|||||||
object SharedResourceIdPatch : ResourcePatch() {
|
object SharedResourceIdPatch : ResourcePatch() {
|
||||||
var AccountSwitcherAccessibility = -1L
|
var AccountSwitcherAccessibility = -1L
|
||||||
var ActionBarRingo = -1L
|
var ActionBarRingo = -1L
|
||||||
|
var ActionBarSearchResultsViewMic = -1L
|
||||||
var AdAttribution = -1L
|
var AdAttribution = -1L
|
||||||
var Appearance = -1L
|
var Appearance = -1L
|
||||||
var AppRelatedEndScreenResults = -1L
|
var AppRelatedEndScreenResults = -1L
|
||||||
@ -92,6 +93,7 @@ object SharedResourceIdPatch : ResourcePatch() {
|
|||||||
|
|
||||||
AccountSwitcherAccessibility = getId(STRING, "account_switcher_accessibility_label")
|
AccountSwitcherAccessibility = getId(STRING, "account_switcher_accessibility_label")
|
||||||
ActionBarRingo = getId(LAYOUT, "action_bar_ringo")
|
ActionBarRingo = getId(LAYOUT, "action_bar_ringo")
|
||||||
|
ActionBarSearchResultsViewMic = getId(LAYOUT, "action_bar_search_results_view_mic")
|
||||||
AdAttribution = getId(ID, "ad_attribution")
|
AdAttribution = getId(ID, "ad_attribution")
|
||||||
Appearance = getId(STRING, "app_theme_appearance_dark")
|
Appearance = getId(STRING, "app_theme_appearance_dark")
|
||||||
AppRelatedEndScreenResults = getId(LAYOUT, "app_related_endscreen_results")
|
AppRelatedEndScreenResults = getId(LAYOUT, "app_related_endscreen_results")
|
||||||
|
@ -82,7 +82,7 @@ Tap here to learn more about DeArrow."</string>
|
|||||||
<string name="revanced_channel_bar_title">Channel bar</string>
|
<string name="revanced_channel_bar_title">Channel bar</string>
|
||||||
<string name="revanced_channel_profile_title">Channel profile</string>
|
<string name="revanced_channel_profile_title">Channel profile</string>
|
||||||
<string name="revanced_comments_title">Comments</string>
|
<string name="revanced_comments_title">Comments</string>
|
||||||
<string name="revanced_custom_filter_strings_summary">Configure which components to filter, separated by new lines.</string>
|
<string name="revanced_custom_filter_strings_summary">List of component path builder strings to filter separated by new line.</string>
|
||||||
<string name="revanced_custom_filter_strings_title">Edit custom filter</string>
|
<string name="revanced_custom_filter_strings_title">Edit custom filter</string>
|
||||||
<string name="revanced_custom_filter_toast_invalid_syntax" formatted="false">Invalid custom filter: %s.</string>
|
<string name="revanced_custom_filter_toast_invalid_syntax" formatted="false">Invalid custom filter: %s.</string>
|
||||||
<string name="revanced_custom_filter_summary_off">Custom filter is disabled.</string>
|
<string name="revanced_custom_filter_summary_off">Custom filter is disabled.</string>
|
||||||
@ -475,6 +475,33 @@ Some components may not be hidden."</string>
|
|||||||
<string name="revanced_hide_join_button_summary_off">Join button is shown.</string>
|
<string name="revanced_hide_join_button_summary_off">Join button is shown.</string>
|
||||||
<string name="revanced_hide_join_button_summary_on">Join button is hidden.</string>
|
<string name="revanced_hide_join_button_summary_on">Join button is hidden.</string>
|
||||||
<string name="revanced_hide_join_button_title">Hide join button</string>
|
<string name="revanced_hide_join_button_title">Hide join button</string>
|
||||||
|
|
||||||
|
<!-- TODO: After refactoring the Preference Screen, rename the settings to be consistent. -->
|
||||||
|
<string name="revanced_hide_keyword_content_screen_title">Hide keyword content</string>
|
||||||
|
<string name="revanced_hide_keyword_content_screen_summary">Hide search and feed videos using keyword filters.</string>
|
||||||
|
<string name="revanced_hide_keyword_content_home_title">Hide home videos by keywords</string>
|
||||||
|
<string name="revanced_hide_keyword_content_home_summary_on">Videos in the home tab are filtered by keywords.</string>
|
||||||
|
<string name="revanced_hide_keyword_content_home_summary_off">Videos in the home tab are not filtered by keywords.</string>
|
||||||
|
<string name="revanced_hide_keyword_content_subscriptions_title">Hide subscription videos by keywords</string>
|
||||||
|
<string name="revanced_hide_keyword_content_subscriptions_summary_on">Videos in the subscriptions tab are filtered by keywords.</string>
|
||||||
|
<string name="revanced_hide_keyword_content_subscriptions_summary_off">Videos in the subscriptions tab are not filtered by keywords.</string>
|
||||||
|
<string name="revanced_hide_keyword_content_search_title">Hide search results by keywords</string>
|
||||||
|
<string name="revanced_hide_keyword_content_search_summary_on">Search results are filtered by keywords.</string>
|
||||||
|
<string name="revanced_hide_keyword_content_search_summary_off">Search results are not filtered by keywords.</string>
|
||||||
|
<string name="revanced_hide_keyword_content_phrases_title">Keywords to hide</string>
|
||||||
|
<string name="revanced_hide_keyword_content_phrases_summary">"Keywords and phrases to hide, separated by new lines.
|
||||||
|
Words with uppercase letters in the middle must be entered with the casing (ie: iPhone, TikTok, LeBlanc)."</string>
|
||||||
|
<string name="revanced_hide_keyword_content_about_title">About keyword filtering</string>
|
||||||
|
<string name="revanced_hide_keyword_content_about_summary">"Home / Subscription / Search results are filtered to hide content that matches keyword phrases.
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
• Some Shorts may not be hidden.
|
||||||
|
• Some UI components may not be hidden.
|
||||||
|
• Searching for a keyword may show no results."</string>
|
||||||
|
<string name="revanced_hide_keyword_toast_invalid_common">Invalid keyword. Cannot use: \'%s\' as a filter</string>
|
||||||
|
<string name="revanced_hide_keyword_toast_invalid_length">Invalid keyword. \'%1$s\' is less than %2$d characters</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="revanced_hide_latest_posts_summary_off">Latest posts are shown.</string>
|
<string name="revanced_hide_latest_posts_summary_off">Latest posts are shown.</string>
|
||||||
<string name="revanced_hide_latest_posts_summary_on">Latest posts are hidden.</string>
|
<string name="revanced_hide_latest_posts_summary_on">Latest posts are hidden.</string>
|
||||||
<string name="revanced_hide_latest_posts_title">Hide latest posts</string>
|
<string name="revanced_hide_latest_posts_title">Hide latest posts</string>
|
||||||
@ -848,12 +875,12 @@ Known issues:
|
|||||||
<string name="revanced_swipe_overlay_text_size_title">Swipe overlay text size</string>
|
<string name="revanced_swipe_overlay_text_size_title">Swipe overlay text size</string>
|
||||||
<string name="revanced_swipe_overlay_timeout_summary">The amount of milliseconds the overlay is visible.</string>
|
<string name="revanced_swipe_overlay_timeout_summary">The amount of milliseconds the overlay is visible.</string>
|
||||||
<string name="revanced_swipe_overlay_timeout_title">Swipe overlay timeout</string>
|
<string name="revanced_swipe_overlay_timeout_title">Swipe overlay timeout</string>
|
||||||
<string name="revanced_switching_create_notification_summary">"Swap the positions of the create button and notification button by spoofing the device's information.
|
<string name="revanced_switch_create_with_notifications_button_summary">"Switch the positions of the create button and notification button by spoofing device information.
|
||||||
|
|
||||||
• Even if you change this setting, it may not take effect until you reboot the device.
|
• Even if you change this setting, it may not take effect until you reboot the device.
|
||||||
• Disabling this setting loads more ads from the server side.
|
• Disabling this setting loads more ads from the server side.
|
||||||
• You should disable this setting to make video ads visible."</string>
|
• You should disable this setting to make video ads visible."</string>
|
||||||
<string name="revanced_switching_create_notification_title">Switch create with notifications button</string>
|
<string name="revanced_switch_create_with_notifications_button_title">Switch create with notifications</string>
|
||||||
<string name="revanced_tool_used">Tool used</string>
|
<string name="revanced_tool_used">Tool used</string>
|
||||||
<string name="revanced_video">Video</string>
|
<string name="revanced_video">Video</string>
|
||||||
|
|
||||||
|
@ -264,6 +264,14 @@
|
|||||||
|
|
||||||
<!-- SETTINGS: HIDE_LAYOUT_COMPONENTS
|
<!-- SETTINGS: HIDE_LAYOUT_COMPONENTS
|
||||||
<PreferenceCategory android:layout="@layout/revanced_settings_preferences_category" android:title="@string/revanced_layout_title" />
|
<PreferenceCategory android:layout="@layout/revanced_settings_preferences_category" android:title="@string/revanced_layout_title" />
|
||||||
|
<PreferenceScreen android:title="@string/revanced_hide_keyword_content_screen_title" android:key="revanced_hide_keyword_content_screen" android:summary="@string/revanced_hide_keyword_content_screen_summary">
|
||||||
|
<PreferenceCategory android:layout="@layout/revanced_settings_preferences_category" android:title="@string/revanced_hide_keyword_content_screen_title" />
|
||||||
|
<SwitchPreference android:title="@string/revanced_hide_keyword_content_home_title" android:key="revanced_hide_keyword_content_home" android:summaryOn="@string/revanced_hide_keyword_content_home_summary_on" android:summaryOff="@string/revanced_hide_keyword_content_home_summary_off" />
|
||||||
|
<SwitchPreference android:title="@string/revanced_hide_keyword_content_subscriptions_title" android:key="revanced_hide_keyword_content_subscriptions" android:summaryOn="@string/revanced_hide_keyword_content_subscriptions_summary_on" android:summaryOff="@string/revanced_hide_keyword_content_subscriptions_summary_off" />
|
||||||
|
<SwitchPreference android:title="@string/revanced_hide_keyword_content_search_title" android:key="revanced_hide_keyword_content_search" android:summaryOn="@string/revanced_hide_keyword_content_search_summary_on" android:summaryOff="@string/revanced_hide_keyword_content_search_summary_off" />
|
||||||
|
<app.revanced.integrations.shared.settings.preference.ResettableEditTextPreference android:title="@string/revanced_hide_keyword_content_phrases_title" android:key="revanced_hide_keyword_content_phrases" android:summary="@string/revanced_hide_keyword_content_phrases_summary" android:inputType="textMultiLine" />
|
||||||
|
<Preference android:title="@string/revanced_hide_keyword_content_about_title" android:selectable="false" android:summary="@string/revanced_hide_keyword_content_about_summary" />
|
||||||
|
</PreferenceScreen>
|
||||||
<SwitchPreference android:title="@string/revanced_custom_filter_title" android:key="revanced_custom_filter" android:defaultValue="false" android:summaryOn="@string/revanced_custom_filter_summary_on" android:summaryOff="@string/revanced_custom_filter_summary_off" />
|
<SwitchPreference android:title="@string/revanced_custom_filter_title" android:key="revanced_custom_filter" android:defaultValue="false" android:summaryOn="@string/revanced_custom_filter_summary_on" android:summaryOff="@string/revanced_custom_filter_summary_off" />
|
||||||
<app.revanced.integrations.shared.settings.preference.ResettableEditTextPreference android:title="@string/revanced_custom_filter_strings_title" android:key="revanced_custom_filter_strings" android:summary="@string/revanced_custom_filter_strings_summary" android:defaultValue="" android:inputType="textMultiLine" />
|
<app.revanced.integrations.shared.settings.preference.ResettableEditTextPreference android:title="@string/revanced_custom_filter_strings_title" android:key="revanced_custom_filter_strings" android:summary="@string/revanced_custom_filter_strings_summary" android:defaultValue="" android:inputType="textMultiLine" />
|
||||||
<SwitchPreference android:title="@string/revanced_hide_album_card_title" android:key="revanced_hide_album_card" android:defaultValue="true" android:summaryOn="@string/revanced_hide_album_card_summary_on" android:summaryOff="@string/revanced_hide_album_card_summary_off" />
|
<SwitchPreference android:title="@string/revanced_hide_album_card_title" android:key="revanced_hide_album_card" android:defaultValue="true" android:summaryOn="@string/revanced_hide_album_card_summary_on" android:summaryOff="@string/revanced_hide_album_card_summary_off" />
|
||||||
@ -554,7 +562,7 @@
|
|||||||
<SwitchPreference android:title="@string/revanced_hide_notifications_button_title" android:key="revanced_hide_notifications_button" android:defaultValue="false" android:summaryOn="@string/revanced_hide_notifications_button_summary_on" android:summaryOff="@string/revanced_hide_notifications_button_summary_off" />
|
<SwitchPreference android:title="@string/revanced_hide_notifications_button_title" android:key="revanced_hide_notifications_button" android:defaultValue="false" android:summaryOn="@string/revanced_hide_notifications_button_summary_on" android:summaryOff="@string/revanced_hide_notifications_button_summary_off" />
|
||||||
<SwitchPreference android:title="@string/revanced_hide_shorts_button_title" android:key="revanced_hide_shorts_button" android:defaultValue="false" android:summaryOn="@string/revanced_hide_shorts_button_summary_on" android:summaryOff="@string/revanced_hide_shorts_button_summary_off" />
|
<SwitchPreference android:title="@string/revanced_hide_shorts_button_title" android:key="revanced_hide_shorts_button" android:defaultValue="false" android:summaryOn="@string/revanced_hide_shorts_button_summary_on" android:summaryOff="@string/revanced_hide_shorts_button_summary_off" />
|
||||||
<SwitchPreference android:title="@string/revanced_hide_subscriptions_button_title" android:key="revanced_hide_subscriptions_button" android:defaultValue="false" android:summaryOn="@string/revanced_hide_subscriptions_button_summary_on" android:summaryOff="@string/revanced_hide_subscriptions_button_summary_off" />
|
<SwitchPreference android:title="@string/revanced_hide_subscriptions_button_title" android:key="revanced_hide_subscriptions_button" android:defaultValue="false" android:summaryOn="@string/revanced_hide_subscriptions_button_summary_on" android:summaryOff="@string/revanced_hide_subscriptions_button_summary_off" />
|
||||||
<SwitchPreference android:title="@string/revanced_switching_create_notification_title" android:key="revanced_switching_create_notification" android:defaultValue="true" android:summary="@string/revanced_switching_create_notification_summary" />SETTINGS: HIDE_NAVIGATION_BUTTONS -->
|
<SwitchPreference android:title="@string/revanced_switch_create_with_notifications_button_title" android:key="revanced_switch_create_with_notifications_button" android:defaultValue="true" android:summary="@string/revanced_switch_create_with_notifications_button_summary" />SETTINGS: HIDE_NAVIGATION_BUTTONS -->
|
||||||
|
|
||||||
<!-- PREFERENCE: NAVIGATION_SETTINGS
|
<!-- PREFERENCE: NAVIGATION_SETTINGS
|
||||||
</PreferenceScreen>PREFERENCE: NAVIGATION_SETTINGS -->
|
</PreferenceScreen>PREFERENCE: NAVIGATION_SETTINGS -->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user