mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-16 06:02:27 +01:00
Compare commits
14 Commits
v4.15.1-de
...
v4.16.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fee2218303 | ||
|
|
1f0b4cdcb4 | ||
|
|
1fd30c1b44 | ||
|
|
fa94ddd510 | ||
|
|
94cf815e4a | ||
|
|
8a3b0610b4 | ||
|
|
b920355d9c | ||
|
|
8b49012130 | ||
|
|
4c7b018878 | ||
|
|
5ddd957313 | ||
|
|
bb0dcbe83d | ||
|
|
163736fb26 | ||
|
|
0c6db43bde | ||
|
|
317e9a80eb |
42
CHANGELOG.md
42
CHANGELOG.md
@@ -1,3 +1,45 @@
|
||||
# [4.16.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v4.16.0-dev.4...v4.16.0-dev.5) (2024-09-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Disable precise seeking gesture:** Hide "pull up" label that shows up when swiping ([#3668](https://github.com/ReVanced/revanced-patches/issues/3668)) ([3fa8af9](https://github.com/ReVanced/revanced-patches/commit/3fa8af9fe534b59ad093c36f1927f56f549a330d))
|
||||
|
||||
# [4.16.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v4.16.0-dev.3...v4.16.0-dev.4) (2024-09-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Soundcloud:** Support latest versions ([#3702](https://github.com/ReVanced/revanced-patches/issues/3702)) ([099ac5e](https://github.com/ReVanced/revanced-patches/commit/099ac5ea2cf55633a7c6a7e6f8e963599bcd5784))
|
||||
|
||||
# [4.16.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v4.16.0-dev.2...v4.16.0-dev.3) (2024-09-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Google Photos:** Restore hidden 'Back up while charging' toggle ([#3678](https://github.com/ReVanced/revanced-patches/issues/3678)) ([f9e19ce](https://github.com/ReVanced/revanced-patches/commit/f9e19ce6e9185fdf31b2b0d5f2934f6e8a544b8e))
|
||||
|
||||
# [4.16.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.16.0-dev.1...v4.16.0-dev.2) (2024-09-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide Shorts components:** Add patch option to hide Shorts from app launcher widget Beta ([#3707](https://github.com/ReVanced/revanced-patches/issues/3707)) ([838f183](https://github.com/ReVanced/revanced-patches/commit/838f1834a5df547ce2c3217b874c0594b6878a67))
|
||||
|
||||
# [4.16.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.15.1-dev.2...v4.16.0-dev.1) (2024-09-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide Shorts components:** Add patch option to hide Shorts app shortcut (long press app icon) ([#3699](https://github.com/ReVanced/revanced-patches/issues/3699)) ([0d4e1f5](https://github.com/ReVanced/revanced-patches/commit/0d4e1f5d03cf3dcc06fd41165e26a1ce901b976b))
|
||||
|
||||
## [4.15.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.15.1-dev.1...v4.15.1-dev.2) (2024-09-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Show video chapter titles without clipping when overlay buttons are enabled ([#3674](https://github.com/ReVanced/revanced-patches/issues/3674)) ([4b88c31](https://github.com/ReVanced/revanced-patches/commit/4b88c316ed90c56e83e2aee266561833b36fc37d))
|
||||
|
||||
## [4.15.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.15.0...v4.15.1-dev.1) (2024-09-23)
|
||||
|
||||
|
||||
|
||||
@@ -321,6 +321,12 @@ public final class app/revanced/patches/googlephotos/misc/integrations/Integrati
|
||||
public static final field INSTANCE Lapp/revanced/patches/googlephotos/misc/integrations/IntegrationsPatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/googlephotos/preferences/RestoreHiddenBackUpWhileChargingTogglePatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/googlephotos/preferences/RestoreHiddenBackUpWhileChargingTogglePatch;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/googlerecorder/restrictions/RemoveDeviceRestrictions;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
@@ -2000,13 +2006,19 @@ public final class app/revanced/patches/youtube/misc/playercontrols/BottomContro
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch;
|
||||
public static field showPlayerControlsFingerprintResult Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
|
||||
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
public final fun getShowPlayerControlsFingerprintResult ()Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
|
||||
public final fun initializeBottomControl (Ljava/lang/String;)V
|
||||
public final fun initializeControl (Ljava/lang/String;)V
|
||||
public final fun injectVisibilityCheckCall (Ljava/lang/String;)V
|
||||
public final fun setShowPlayerControlsFingerprintResult (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable {
|
||||
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch;
|
||||
public final fun addBottomControls (Ljava/lang/String;)V
|
||||
public fun close ()V
|
||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch : app/revanced/patcher/patch/BytecodePatch {
|
||||
@@ -2174,6 +2186,8 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
||||
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
|
||||
public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstWideLiteralInstructionValueReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfFirstWideLiteralInstructionValueReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
|
||||
public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
||||
@@ -2201,6 +2215,7 @@ public final class app/revanced/util/ResourceUtilsKt {
|
||||
public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/DomFileEditor;Lapp/revanced/patcher/util/DomFileEditor;)Ljava/lang/AutoCloseable;
|
||||
public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
||||
public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
|
||||
public static final fun insertFirst (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)V
|
||||
public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.parallel = true
|
||||
org.gradle.caching = true
|
||||
kotlin.code.style = official
|
||||
version = 4.15.1-dev.1
|
||||
version = 4.16.0-dev.5
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package app.revanced.patches.googlephotos.preferences
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.googlephotos.preferences.fingerprints.BackupPreferencesFingerprint
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
name = "Restore hidden 'Back up while charging' toggle",
|
||||
description = "Restores a hidden toggle to only run backups when the device is charging.",
|
||||
compatiblePackages = [CompatiblePackage("com.google.android.apps.photos")],
|
||||
)
|
||||
@Suppress("unused")
|
||||
object RestoreHiddenBackUpWhileChargingTogglePatch : BytecodePatch(
|
||||
setOf(BackupPreferencesFingerprint),
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// Patches 'backup_prefs_had_backup_only_when_charging_enabled' to always be true.
|
||||
BackupPreferencesFingerprint.result?.let {
|
||||
val chargingPrefStringIndex = it.scanResult.stringsScanResult!!.matches.first().index
|
||||
it.mutableMethod.apply {
|
||||
// Get the register of move-result.
|
||||
val resultRegister = getInstruction<OneRegisterInstruction>(chargingPrefStringIndex + 2).registerA
|
||||
// Insert const after move-result to override register as true.
|
||||
addInstruction(chargingPrefStringIndex + 3, "const/4 v$resultRegister, 0x1")
|
||||
}
|
||||
} ?: throw Exception("BackupPreferencesFingerprint result not found")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package app.revanced.patches.googlephotos.preferences.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
|
||||
internal object BackupPreferencesFingerprint : MethodFingerprint(
|
||||
returnType = "Lcom/google/android/apps/photos/backup/data/BackupPreferences;",
|
||||
strings = listOf(
|
||||
"backup_prefs_had_backup_only_when_charging_enabled",
|
||||
),
|
||||
)
|
||||
@@ -9,6 +9,7 @@ import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.getNode
|
||||
import app.revanced.util.insertFirst
|
||||
import org.w3c.dom.Node
|
||||
import java.io.Closeable
|
||||
|
||||
@@ -47,11 +48,7 @@ abstract class BaseSettingsResourcePatch(
|
||||
// It may be necessary to ask for the desired resourceValue in the future.
|
||||
AddResourcesPatch("values", resource)
|
||||
}.let { preferenceNode ->
|
||||
if (prepend && firstChild != null) {
|
||||
insertBefore(preferenceNode, firstChild)
|
||||
} else {
|
||||
appendChild(preferenceNode)
|
||||
}
|
||||
insertFirst(preferenceNode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,4 @@ internal object InterceptFingerprint : MethodFingerprint(
|
||||
Opcode.MOVE_RESULT_OBJECT
|
||||
),
|
||||
strings = listOf("SC-Mob-UserPlan", "Configuration"),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.sourceFile == "ApiUserPlanInterceptor.java" ||
|
||||
classDef.sourceFile == "ApiUserPlanInterceptor.kt"
|
||||
},
|
||||
)
|
||||
|
||||
@@ -8,7 +8,4 @@ internal object UserConsumerPlanConstructorFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("Ljava/lang/String;", "Z", "Ljava/lang/String;", "Ljava/util/List;", "Ljava/lang/String;", "Ljava/lang/String;"),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.sourceFile == "UserConsumerPlan.kt"
|
||||
},
|
||||
)
|
||||
@@ -7,6 +7,7 @@ internal object CreateTrackingApiFingerprint : MethodFingerprint(
|
||||
returnType = "L",
|
||||
accessFlags = AccessFlags.PUBLIC.value,
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
classDef.sourceFile == "DefaultTrackingApiFactory.kt" && methodDef.name == "create"
|
||||
methodDef.name == "create"
|
||||
},
|
||||
strings = listOf("backend", "boogaloo")
|
||||
)
|
||||
|
||||
@@ -15,7 +15,5 @@ internal object DownloadOperationsHeaderVerificationFingerprint : MethodFingerpr
|
||||
Opcode.MOVE_RESULT_OBJECT,
|
||||
Opcode.CONST_STRING
|
||||
),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.sourceFile == "DownloadOperations.kt"
|
||||
}
|
||||
strings = listOf("X-SC-Mime-Type", "X-SC-Preset", "X-SC-Quality")
|
||||
)
|
||||
@@ -14,7 +14,4 @@ internal object DownloadOperationsURLBuilderFingerprint : MethodFingerprint(
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.FILLED_NEW_ARRAY
|
||||
),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.sourceFile == "DownloadOperations.kt"
|
||||
}
|
||||
)
|
||||
@@ -3,12 +3,16 @@ package app.revanced.patches.soundcloud.shared.fingerprints
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
|
||||
internal object FeatureConstructorFingerprint : MethodFingerprint(
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
parameters = listOf("Ljava/lang/String;", "Z", "Ljava/util/List;"),
|
||||
customFingerprint = { _, classDef ->
|
||||
classDef.sourceFile == "Feature.kt"
|
||||
},
|
||||
opcodes = listOf(
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.INVOKE_VIRTUAL
|
||||
)
|
||||
)
|
||||
@@ -51,8 +51,8 @@ object CopyVideoUrlBytecodePatch : BytecodePatch(emptySet()) {
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
BUTTONS_DESCRIPTORS.forEach { descriptor ->
|
||||
PlayerControlsBytecodePatch.initializeControl("$descriptor->initializeButton(Landroid/view/View;)V")
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$descriptor->changeVisibility(Z)V")
|
||||
PlayerControlsBytecodePatch.initializeBottomControl(descriptor)
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall(descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
|
||||
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
@@ -13,7 +13,7 @@ import app.revanced.util.copyResources
|
||||
@Patch(
|
||||
dependencies = [
|
||||
SettingsPatch::class,
|
||||
BottomControlsResourcePatch::class,
|
||||
PlayerControlsResourcePatch::class,
|
||||
AddResourcesPatch::class
|
||||
]
|
||||
)
|
||||
@@ -34,6 +34,6 @@ internal object CopyVideoUrlResourcePatch : ResourcePatch() {
|
||||
)
|
||||
)
|
||||
|
||||
BottomControlsResourcePatch.addControls("copyvideourl")
|
||||
PlayerControlsResourcePatch.addBottomControls("copyvideourl")
|
||||
}
|
||||
}
|
||||
@@ -58,8 +58,8 @@ object DownloadsPatch : BytecodePatch(
|
||||
private const val BUTTON_DESCRIPTOR = "Lapp/revanced/integrations/youtube/videoplayer/ExternalDownloadButton;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
PlayerControlsBytecodePatch.initializeControl("$BUTTON_DESCRIPTOR->initializeButton(Landroid/view/View;)V")
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$BUTTON_DESCRIPTOR->changeVisibility(Z)V")
|
||||
PlayerControlsBytecodePatch.initializeBottomControl(BUTTON_DESCRIPTOR)
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall(BUTTON_DESCRIPTOR)
|
||||
|
||||
// Main activity is used to launch downloader intent.
|
||||
MainActivityFingerprint.resultOrThrow().mutableMethod.apply {
|
||||
|
||||
@@ -9,14 +9,14 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen
|
||||
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.TextPreference
|
||||
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
|
||||
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
|
||||
@Patch(
|
||||
dependencies = [
|
||||
BottomControlsResourcePatch::class,
|
||||
PlayerControlsResourcePatch::class,
|
||||
SettingsPatch::class,
|
||||
AddResourcesPatch::class,
|
||||
],
|
||||
@@ -42,6 +42,6 @@ internal object DownloadsResourcePatch : ResourcePatch() {
|
||||
ResourceGroup("drawable", "revanced_yt_download_button.xml"),
|
||||
)
|
||||
|
||||
BottomControlsResourcePatch.addControls("downloads")
|
||||
PlayerControlsResourcePatch.addBottomControls("downloads")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
package app.revanced.patches.youtube.interaction.seekbar
|
||||
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
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.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.IsSwipingUpFingerprint
|
||||
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.AllowSwipingUpGestureFingerprint
|
||||
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.ShowSwipingUpGuideFingerprint
|
||||
import app.revanced.patches.youtube.interaction.seekbar.fingerprints.SwipingUpGestureParentFingerprint
|
||||
import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import app.revanced.util.alsoResolve
|
||||
|
||||
@Patch(
|
||||
name = "Disable precise seeking gesture",
|
||||
@@ -52,11 +54,10 @@ import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
)
|
||||
@Suppress("unused")
|
||||
object DisablePreciseSeekingGesturePatch : BytecodePatch(
|
||||
setOf(IsSwipingUpFingerprint)
|
||||
setOf(SwipingUpGestureParentFingerprint)
|
||||
) {
|
||||
private const val INTEGRATIONS_METHOD_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/patches/DisablePreciseSeekingGesturePatch;->" +
|
||||
"disableGesture(Landroid/view/VelocityTracker;Landroid/view/MotionEvent;)V"
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/patches/DisablePreciseSeekingGesturePatch;"
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
AddResourcesPatch(this::class)
|
||||
@@ -65,19 +66,37 @@ object DisablePreciseSeekingGesturePatch : BytecodePatch(
|
||||
SwitchPreference("revanced_disable_precise_seeking_gesture")
|
||||
)
|
||||
|
||||
IsSwipingUpFingerprint.result?.let {
|
||||
val addMovementIndex = it.scanResult.patternScanResult!!.startIndex - 1
|
||||
AllowSwipingUpGestureFingerprint.alsoResolve(
|
||||
context,
|
||||
SwipingUpGestureParentFingerprint
|
||||
).mutableMethod.apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->isGestureDisabled()Z
|
||||
move-result v0
|
||||
if-eqz v0, :disabled
|
||||
return-void
|
||||
""",
|
||||
ExternalLabel("disabled", getInstruction(0))
|
||||
)
|
||||
}
|
||||
|
||||
it.mutableMethod.apply {
|
||||
val addMovementInstruction = getInstruction<FiveRegisterInstruction>(addMovementIndex)
|
||||
val trackerRegister = addMovementInstruction.registerC
|
||||
val eventRegister = addMovementInstruction.registerD
|
||||
|
||||
replaceInstruction(
|
||||
addMovementIndex,
|
||||
"invoke-static {v$trackerRegister, v$eventRegister}, $INTEGRATIONS_METHOD_DESCRIPTOR"
|
||||
)
|
||||
}
|
||||
} ?: throw IsSwipingUpFingerprint.exception
|
||||
ShowSwipingUpGuideFingerprint.alsoResolve(
|
||||
context,
|
||||
SwipingUpGestureParentFingerprint
|
||||
).mutableMethod.apply {
|
||||
addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
invoke-static { }, $INTEGRATIONS_CLASS_DESCRIPTOR->isGestureDisabled()Z
|
||||
move-result v0
|
||||
if-eqz v0, :disabled
|
||||
const/4 v0, 0x0
|
||||
return v0
|
||||
""",
|
||||
ExternalLabel("disabled", getInstruction(0))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
/**
|
||||
* Resolves using the class found in [SwipingUpGestureParentFingerprint].
|
||||
*/
|
||||
internal object AllowSwipingUpGestureFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf("L"),
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object IsSwipingUpFingerprint : MethodFingerprint(
|
||||
returnType = "Z",
|
||||
parameters = listOf("Landroid/view/MotionEvent;", "J"),
|
||||
opcodes = listOf(
|
||||
Opcode.SGET_OBJECT,
|
||||
Opcode.IGET_OBJECT
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
|
||||
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
/**
|
||||
* Resolves using the class found in [SwipingUpGestureParentFingerprint].
|
||||
*/
|
||||
internal object ShowSwipingUpGuideFingerprint : LiteralValueFingerprint(
|
||||
accessFlags = AccessFlags.FINAL.value,
|
||||
returnType = "Z",
|
||||
parameters = emptyList(),
|
||||
literalSupplier = { 1L }
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package app.revanced.patches.youtube.interaction.seekbar.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object SwipingUpGestureParentFingerprint : LiteralValueFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "Z",
|
||||
parameters = listOf(),
|
||||
literalSupplier = { 45379021 }
|
||||
)
|
||||
@@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||
import app.revanced.patches.youtube.layout.hide.shorts.fingerprints.*
|
||||
@@ -76,6 +77,20 @@ object HideShortsComponentsPatch : BytecodePatch(
|
||||
) {
|
||||
private const val FILTER_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/youtube/patches/components/ShortsFilter;"
|
||||
|
||||
internal val hideShortsAppShortcut by booleanPatchOption(
|
||||
key = "hideShortsAppShortcut",
|
||||
default = false,
|
||||
title = "Hide Shorts app shortcut",
|
||||
description = "Permanently hides the shortcut to open Shorts when long pressing the app icon in your launcher."
|
||||
)
|
||||
|
||||
internal val hideShortsWidget by booleanPatchOption(
|
||||
key = "hideShortsWidget",
|
||||
default = false,
|
||||
title = "Hide Shorts widget",
|
||||
description = "Permanently hides the launcher widget Shorts button."
|
||||
)
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// region Hide the Shorts shelf.
|
||||
|
||||
|
||||
@@ -6,7 +6,11 @@ import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsAppShortcut
|
||||
import app.revanced.patches.youtube.layout.hide.shorts.HideShortsComponentsPatch.hideShortsWidget
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||
import org.w3c.dom.Element
|
||||
|
||||
@Patch(dependencies = [SettingsPatch::class, ResourceMappingPatch::class, AddResourcesPatch::class])
|
||||
object HideShortsComponentsResourcePatch : ResourcePatch() {
|
||||
@@ -52,6 +56,28 @@ object HideShortsComponentsResourcePatch : ResourcePatch() {
|
||||
SwitchPreference("revanced_hide_shorts_navigation_bar"),
|
||||
)
|
||||
|
||||
if (hideShortsAppShortcut == true) {
|
||||
context.xmlEditor["res/xml/main_shortcuts.xml"].use { editor ->
|
||||
val shortsItem = editor.file.childNodes.findElementByAttributeValueOrThrow(
|
||||
"android:shortcutId",
|
||||
"shorts-shortcut"
|
||||
)
|
||||
|
||||
shortsItem.parentNode.removeChild(shortsItem)
|
||||
}
|
||||
}
|
||||
|
||||
if (hideShortsWidget == true) {
|
||||
context.xmlEditor["res/layout/appwidget_two_rows.xml"].use { editor ->
|
||||
val shortsItem = editor.file.childNodes.findElementByAttributeValueOrThrow(
|
||||
"android:id",
|
||||
"@id/button_shorts_container"
|
||||
)
|
||||
|
||||
shortsItem.parentNode.removeChild(shortsItem)
|
||||
}
|
||||
}
|
||||
|
||||
reelPlayerRightCellButtonHeight = ResourceMappingPatch[
|
||||
"dimen",
|
||||
"reel_player_right_cell_button_height",
|
||||
|
||||
@@ -10,7 +10,6 @@ import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.AppendTimeFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.ControlsOverlayFingerprint
|
||||
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.RectangleFieldInvalidatorFingerprint
|
||||
@@ -26,7 +25,10 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
|
||||
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
|
||||
import app.revanced.util.exception
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.*
|
||||
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.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
@@ -169,59 +171,14 @@ object SponsorBlockBytecodePatch : BytecodePatch(
|
||||
break
|
||||
}
|
||||
|
||||
/*
|
||||
* Voting & Shield button
|
||||
*/
|
||||
val controlsMethodResult = PlayerControlsBytecodePatch.showPlayerControlsFingerprintResult
|
||||
// Change visibility of the buttons.
|
||||
PlayerControlsBytecodePatch.initializeTopControl(INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall(INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
|
||||
|
||||
val controlsLayoutStubResourceId =
|
||||
ResourceMappingPatch["id", "controls_layout_stub"]
|
||||
val zoomOverlayResourceId =
|
||||
ResourceMappingPatch["id", "video_zoom_overlay_stub"]
|
||||
PlayerControlsBytecodePatch.initializeTopControl(INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall(INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
|
||||
|
||||
methods@ for (method in controlsMethodResult.mutableClass.methods) {
|
||||
val instructions = method.implementation?.instructions!!
|
||||
instructions@ for ((index, instruction) in instructions.withIndex()) {
|
||||
// search for method which inflates the controls layout view
|
||||
if (instruction.opcode != Opcode.CONST) continue@instructions
|
||||
|
||||
when ((instruction as NarrowLiteralInstruction).wideLiteral) {
|
||||
controlsLayoutStubResourceId -> {
|
||||
// replace the view with the YouTubeControlsOverlay
|
||||
val moveResultInstructionIndex = index + 5
|
||||
val inflatedViewRegister =
|
||||
(instructions[moveResultInstructionIndex] as OneRegisterInstruction).registerA
|
||||
// initialize with the player overlay object
|
||||
method.addInstructions(
|
||||
moveResultInstructionIndex + 1, // insert right after moving the view to the register and use that register
|
||||
"""
|
||||
invoke-static {v$inflatedViewRegister}, $INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/View;)V
|
||||
invoke-static {v$inflatedViewRegister}, $INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/View;)V
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
zoomOverlayResourceId -> {
|
||||
val invertVisibilityMethod =
|
||||
context.toMethodWalker(method).nextMethod(index - 6, true).getMethod() as MutableMethod
|
||||
// change visibility of the buttons
|
||||
invertVisibilityMethod.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static {p1}, $INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibilityNegatedImmediate(Z)V
|
||||
invoke-static {p1}, $INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibilityNegatedImmediate(Z)V
|
||||
""".trimIndent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// change visibility of the buttons
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibility(Z)V")
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibility(Z)V")
|
||||
|
||||
// append the new time to the player layout
|
||||
// Append the new time to the player layout.
|
||||
val appendTimeFingerprintResult = AppendTimeFingerprint.result!!
|
||||
val appendTimePatternScanStartIndex = appendTimeFingerprintResult.scanResult.patternScanResult!!.startIndex
|
||||
val targetRegister =
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package app.revanced.patches.youtube.layout.sponsorblock
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.all.misc.resources.AddResourcesPatch
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||
import app.revanced.patches.youtube.misc.settings.SettingsResourcePatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.copyXmlNode
|
||||
import app.revanced.util.inputStreamFromBundledResource
|
||||
|
||||
@Patch(
|
||||
dependencies = [
|
||||
@@ -60,49 +58,6 @@ internal object SponsorBlockResourcePatch : ResourcePatch() {
|
||||
context.copyResources("sponsorblock", resourceGroup)
|
||||
}
|
||||
|
||||
// copy nodes from host resources to their real xml files
|
||||
|
||||
val hostingResourceStream =
|
||||
inputStreamFromBundledResource(
|
||||
"sponsorblock",
|
||||
"host/layout/youtube_controls_layout.xml",
|
||||
)!!
|
||||
|
||||
var modifiedControlsLayout = false
|
||||
val editor = context.xmlEditor["res/layout/youtube_controls_layout.xml"]
|
||||
"RelativeLayout".copyXmlNode(
|
||||
context.xmlEditor[hostingResourceStream],
|
||||
editor,
|
||||
).also {
|
||||
val document = editor.file
|
||||
|
||||
val children = document.getElementsByTagName("RelativeLayout").item(0).childNodes
|
||||
|
||||
// Replace the startOf with the voting button view so that the button does not overlap
|
||||
for (i in 1 until children.length) {
|
||||
val view = children.item(i)
|
||||
|
||||
// Replace the attribute for a specific node only
|
||||
if (!(
|
||||
view.hasAttributes() &&
|
||||
view.attributes.getNamedItem(
|
||||
"android:id",
|
||||
).nodeValue.endsWith("live_chat_overlay_button")
|
||||
)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
// voting button id from the voting button view from the youtube_controls_layout.xml host file
|
||||
val votingButtonId = "@+id/revanced_sb_voting_button"
|
||||
|
||||
view.attributes.getNamedItem("android:layout_toStartOf").nodeValue = votingButtonId
|
||||
|
||||
modifiedControlsLayout = true
|
||||
break
|
||||
}
|
||||
}.close()
|
||||
|
||||
if (!modifiedControlsLayout) throw PatchException("Could not modify controls layout")
|
||||
PlayerControlsResourcePatch.addTopControls("sponsorblock")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,70 +3,18 @@ package app.revanced.patches.youtube.misc.playercontrols
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.DomFileEditor
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||
import java.io.Closeable
|
||||
|
||||
@Patch(dependencies = [ResourceMappingPatch::class])
|
||||
@Patch(
|
||||
dependencies = [PlayerControlsBytecodePatch::class],
|
||||
)
|
||||
@Deprecated("Patch renamed to PlayerControlsResourcePatch", replaceWith = ReplaceWith("PlayerControlsBytecodePatch"))
|
||||
object BottomControlsResourcePatch : ResourcePatch(), Closeable {
|
||||
internal var bottomUiContainerResourceId: Long = -1
|
||||
override fun execute(context: ResourceContext) {}
|
||||
|
||||
private const val TARGET_RESOURCE_NAME = "youtube_controls_bottom_ui_container.xml"
|
||||
private const val TARGET_RESOURCE = "res/layout/$TARGET_RESOURCE_NAME"
|
||||
|
||||
// The element to the left of the element being added.
|
||||
private var lastLeftOf = "fullscreen_button"
|
||||
|
||||
private lateinit var resourceContext: ResourceContext
|
||||
private lateinit var targetDocumentEditor: DomFileEditor
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
resourceContext = context
|
||||
targetDocumentEditor = context.xmlEditor[TARGET_RESOURCE]
|
||||
|
||||
bottomUiContainerResourceId = ResourceMappingPatch["id", "bottom_ui_container_stub"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new controls to the bottom of the YouTube player.
|
||||
*
|
||||
* @param resourceDirectoryName The name of the directory containing the hosting resource.
|
||||
*/
|
||||
fun addControls(resourceDirectoryName: String) {
|
||||
val sourceDocumentEditor = resourceContext.xmlEditor[
|
||||
this::class.java.classLoader.getResourceAsStream(
|
||||
"$resourceDirectoryName/host/layout/$TARGET_RESOURCE_NAME",
|
||||
)!!,
|
||||
]
|
||||
val sourceDocument = sourceDocumentEditor.file
|
||||
val targetDocument = targetDocumentEditor.file
|
||||
|
||||
val targetElementTag = "android.support.constraint.ConstraintLayout"
|
||||
|
||||
val sourceElements = sourceDocument.getElementsByTagName(targetElementTag).item(0).childNodes
|
||||
val targetElement = targetDocument.getElementsByTagName(targetElementTag).item(0)
|
||||
|
||||
for (index in 1 until sourceElements.length) {
|
||||
val element = sourceElements.item(index).cloneNode(true)
|
||||
|
||||
// If the element has no attributes there's no point to adding it to the destination.
|
||||
if (!element.hasAttributes()) continue
|
||||
|
||||
// Set the elements lastLeftOf attribute to the lastLeftOf value.
|
||||
val namespace = "@+id"
|
||||
element.attributes.getNamedItem("yt:layout_constraintRight_toLeftOf").nodeValue =
|
||||
"$namespace/$lastLeftOf"
|
||||
|
||||
// Set lastLeftOf attribute to the current element.
|
||||
val nameSpaceLength = 5
|
||||
lastLeftOf = element.attributes.getNamedItem("android:id").nodeValue.substring(nameSpaceLength)
|
||||
|
||||
// Add the element.
|
||||
targetDocument.adoptNode(element)
|
||||
targetElement.appendChild(element)
|
||||
}
|
||||
sourceDocumentEditor.close()
|
||||
PlayerControlsResourcePatch.addBottomControls(resourceDirectoryName)
|
||||
}
|
||||
|
||||
override fun close() = targetDocumentEditor.close()
|
||||
}
|
||||
override fun close() {}
|
||||
}
|
||||
@@ -1,65 +1,144 @@
|
||||
package app.revanced.patches.youtube.misc.playercontrols
|
||||
|
||||
import app.revanced.util.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprintResult
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.youtube.shared.fingerprints.LayoutConstructorFingerprint
|
||||
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.BottomControlsInflateFingerprint
|
||||
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerControlsVisibilityFingerprint
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.ControlsOverlayVisibility
|
||||
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.OverlayViewInflateFingerprint
|
||||
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerBottomControlsInflateFingerprint
|
||||
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerControlsIntegrationHookFingerprint
|
||||
import app.revanced.patches.youtube.misc.playercontrols.fingerprints.PlayerTopControlsInflateFingerprint
|
||||
import app.revanced.util.alsoResolve
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.indexOfFirstWideLiteralInstructionValueReversedOrThrow
|
||||
import app.revanced.util.resultOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
|
||||
@Patch(
|
||||
description = "Manages the code for the player controls of the YouTube player.",
|
||||
dependencies = [BottomControlsResourcePatch::class],
|
||||
dependencies = [PlayerControlsResourcePatch::class],
|
||||
)
|
||||
object PlayerControlsBytecodePatch : BytecodePatch(
|
||||
setOf(LayoutConstructorFingerprint, BottomControlsInflateFingerprint)
|
||||
setOf(
|
||||
PlayerTopControlsInflateFingerprint,
|
||||
PlayerBottomControlsInflateFingerprint,
|
||||
OverlayViewInflateFingerprint,
|
||||
PlayerControlsIntegrationHookFingerprint
|
||||
)
|
||||
) {
|
||||
lateinit var showPlayerControlsFingerprintResult: MethodFingerprintResult
|
||||
private const val INTEGRATIONS_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/integrations/youtube/patches/PlayerControlsPatch;"
|
||||
|
||||
private var moveToRegisterInstructionIndex: Int = 0
|
||||
private var viewRegister: Int = 0
|
||||
private lateinit var inflateFingerprintResult: MethodFingerprintResult
|
||||
private lateinit var inflateTopControlMethod: MutableMethod
|
||||
private var inflateTopControlInsertIndex: Int = -1
|
||||
private var inflateTopControlRegister: Int = -1
|
||||
|
||||
private lateinit var inflateBottomControlMethod: MutableMethod
|
||||
private var inflateBottomControlInsertIndex: Int = -1
|
||||
private var inflateBottomControlRegister: Int = -1
|
||||
|
||||
private lateinit var visibilityMethod: MutableMethod
|
||||
private var visibilityInsertIndex: Int = 0
|
||||
|
||||
private lateinit var visibilityImmediateMethod: MutableMethod
|
||||
private var visibilityImmediateInsertIndex: Int = 0
|
||||
|
||||
override fun execute(context: BytecodeContext) {
|
||||
LayoutConstructorFingerprint.result?.let {
|
||||
if (!PlayerControlsVisibilityFingerprint.resolve(context, it.classDef))
|
||||
throw LayoutConstructorFingerprint.exception
|
||||
} ?: throw LayoutConstructorFingerprint.exception
|
||||
fun MutableMethod.indexOfFirstViewInflateOrThrow() =
|
||||
indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Landroid/view/ViewStub;" &&
|
||||
reference.name == "inflate"
|
||||
}
|
||||
|
||||
showPlayerControlsFingerprintResult = PlayerControlsVisibilityFingerprint.result!!
|
||||
PlayerBottomControlsInflateFingerprint.resultOrThrow().mutableMethod.apply{
|
||||
inflateBottomControlMethod = this
|
||||
|
||||
inflateFingerprintResult = BottomControlsInflateFingerprint.result!!.also {
|
||||
moveToRegisterInstructionIndex = it.scanResult.patternScanResult!!.endIndex
|
||||
viewRegister =
|
||||
(it.mutableMethod.implementation!!.instructions[moveToRegisterInstructionIndex] as OneRegisterInstruction).registerA
|
||||
val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1
|
||||
inflateBottomControlRegister = getInstruction<OneRegisterInstruction>(inflateReturnObjectIndex).registerA
|
||||
inflateBottomControlInsertIndex = inflateReturnObjectIndex + 1
|
||||
}
|
||||
|
||||
PlayerTopControlsInflateFingerprint.resultOrThrow().mutableMethod.apply {
|
||||
inflateTopControlMethod = this
|
||||
|
||||
val inflateReturnObjectIndex = indexOfFirstViewInflateOrThrow() + 1
|
||||
inflateTopControlRegister = getInstruction<OneRegisterInstruction>(inflateReturnObjectIndex).registerA
|
||||
inflateTopControlInsertIndex = inflateReturnObjectIndex + 1
|
||||
}
|
||||
|
||||
ControlsOverlayVisibility.alsoResolve(
|
||||
context, PlayerTopControlsInflateFingerprint
|
||||
).mutableMethod.apply {
|
||||
visibilityMethod = this
|
||||
}
|
||||
|
||||
// Hook the fullscreen close button. Used to fix visibility
|
||||
// when seeking and other situations.
|
||||
OverlayViewInflateFingerprint.resultOrThrow().mutableMethod.apply {
|
||||
val resourceIndex = indexOfFirstWideLiteralInstructionValueReversedOrThrow(
|
||||
PlayerControlsResourcePatch.fullscreenButton
|
||||
)
|
||||
|
||||
val index = indexOfFirstInstructionOrThrow(resourceIndex) {
|
||||
opcode == Opcode.CHECK_CAST && getReference<TypeReference>()?.type ==
|
||||
"Landroid/widget/ImageView;"
|
||||
}
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstruction(index + 1, "invoke-static { v$register }, " +
|
||||
"$INTEGRATIONS_CLASS_DESCRIPTOR->setFullscreenCloseButton(Landroid/widget/ImageView;)V")
|
||||
}
|
||||
|
||||
visibilityImmediateMethod = PlayerControlsIntegrationHookFingerprint.resultOrThrow().mutableMethod
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the code to change the visibility of controls.
|
||||
* Injects the code to initialize the controls.
|
||||
* @param descriptor The descriptor of the method which should be called.
|
||||
*/
|
||||
fun injectVisibilityCheckCall(descriptor: String) {
|
||||
showPlayerControlsFingerprintResult.mutableMethod.addInstruction(
|
||||
0,
|
||||
"""
|
||||
invoke-static {p1}, $descriptor
|
||||
"""
|
||||
internal fun initializeTopControl(descriptor: String) {
|
||||
inflateTopControlMethod.addInstruction(
|
||||
inflateTopControlInsertIndex++,
|
||||
"invoke-static { v$inflateTopControlRegister }, $descriptor->initialize(Landroid/view/View;)V"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the code to initialize the controls.
|
||||
* @param descriptor The descriptor of the method which should be calleed.
|
||||
* @param descriptor The descriptor of the method which should be called.
|
||||
*/
|
||||
fun initializeControl(descriptor: String) {
|
||||
inflateFingerprintResult.mutableMethod.addInstruction(
|
||||
moveToRegisterInstructionIndex + 1,
|
||||
"invoke-static {v$viewRegister}, $descriptor"
|
||||
fun initializeBottomControl(descriptor: String) {
|
||||
inflateBottomControlMethod.addInstruction(
|
||||
inflateBottomControlInsertIndex++,
|
||||
"invoke-static { v$inflateBottomControlRegister }, $descriptor->initializeButton(Landroid/view/View;)V"
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Injects the code to change the visibility of controls.
|
||||
* @param descriptor The descriptor of the method which should be called.
|
||||
*/
|
||||
fun injectVisibilityCheckCall(descriptor: String) {
|
||||
visibilityMethod.addInstruction(
|
||||
visibilityInsertIndex++,
|
||||
"invoke-static { p1 , p2 }, $descriptor->changeVisibility(ZZ)V"
|
||||
)
|
||||
|
||||
visibilityImmediateMethod.addInstruction(
|
||||
visibilityImmediateInsertIndex++,
|
||||
"invoke-static { p0 }, $descriptor->changeVisibilityImmediate(Z)V"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Deprecated("Obsolete", replaceWith = ReplaceWith("initializeBottomControl"))
|
||||
fun initializeControl(descriptor: String)= initializeBottomControl(descriptor)
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package app.revanced.patches.youtube.misc.playercontrols
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.DomFileEditor
|
||||
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
|
||||
import app.revanced.util.copyXmlNode
|
||||
import app.revanced.util.findElementByAttributeValue
|
||||
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||
import app.revanced.util.inputStreamFromBundledResource
|
||||
import org.w3c.dom.Node
|
||||
import java.io.Closeable
|
||||
|
||||
@Patch(dependencies = [ResourceMappingPatch::class])
|
||||
object PlayerControlsResourcePatch : ResourcePatch(), Closeable {
|
||||
private const val TARGET_RESOURCE_NAME = "youtube_controls_bottom_ui_container.xml"
|
||||
private const val TARGET_RESOURCE = "res/layout/$TARGET_RESOURCE_NAME"
|
||||
|
||||
internal var bottomUiContainerResourceId: Long = -1L
|
||||
internal var controlsLayoutStub: Long = -1L
|
||||
internal var heatseekerViewstub = -1L
|
||||
internal var fullscreenButton = -1L
|
||||
|
||||
private lateinit var resourceContext: ResourceContext
|
||||
|
||||
/**
|
||||
* The element to the left of the element being added.
|
||||
*/
|
||||
private var bottomLastLeftOf = "@id/fullscreen_button"
|
||||
private lateinit var bottomInsertBeforeNode: Node
|
||||
private lateinit var bottomTargetDocumentEditor: DomFileEditor
|
||||
private lateinit var bottomTargetElement : Node
|
||||
|
||||
override fun execute(context: ResourceContext) {
|
||||
bottomUiContainerResourceId = ResourceMappingPatch["id", "bottom_ui_container_stub"]
|
||||
controlsLayoutStub = ResourceMappingPatch["id", "controls_layout_stub"]
|
||||
heatseekerViewstub = ResourceMappingPatch["id", "heatseeker_viewstub"]
|
||||
fullscreenButton = ResourceMappingPatch["id", "fullscreen_button"]
|
||||
|
||||
resourceContext = context
|
||||
bottomTargetDocumentEditor = context.xmlEditor[TARGET_RESOURCE]
|
||||
val document = bottomTargetDocumentEditor.file
|
||||
|
||||
bottomTargetElement = document.getElementsByTagName(
|
||||
"android.support.constraint.ConstraintLayout"
|
||||
).item(0)
|
||||
|
||||
bottomInsertBeforeNode = document.childNodes.findElementByAttributeValue(
|
||||
"android:inflatedId",
|
||||
bottomLastLeftOf
|
||||
) ?: document.childNodes.findElementByAttributeValueOrThrow(
|
||||
"android:id", // Older targets use non inflated id.
|
||||
bottomLastLeftOf
|
||||
)
|
||||
}
|
||||
|
||||
// Internal until this is modified to work with any patch (and not just SponsorBlock).
|
||||
internal fun addTopControls(resourceDirectoryName: String) {
|
||||
val hostingResourceStream = inputStreamFromBundledResource(
|
||||
resourceDirectoryName,
|
||||
"host/layout/youtube_controls_layout.xml",
|
||||
)!!
|
||||
|
||||
val editor = resourceContext.xmlEditor["res/layout/youtube_controls_layout.xml"]
|
||||
|
||||
"RelativeLayout".copyXmlNode(
|
||||
resourceContext.xmlEditor[hostingResourceStream],
|
||||
editor,
|
||||
).use {
|
||||
val document = editor.file
|
||||
val children = document.getElementsByTagName("RelativeLayout").item(0).childNodes
|
||||
|
||||
// Replace the startOf with the voting button view so that the button does not overlap
|
||||
for (index in 1 until children.length) {
|
||||
val view = children.item(index)
|
||||
|
||||
// FIXME: This uses hard coded values that only works with SponsorBlock.
|
||||
// If other top buttons are added by other patches, this code must be changed.
|
||||
if (view.hasAttributes() && view.attributes.getNamedItem("android:id")
|
||||
.nodeValue.endsWith("live_chat_overlay_button")
|
||||
) {
|
||||
// voting button id from the voting button view from the youtube_controls_layout.xml host file
|
||||
val votingButtonId = "@+id/revanced_sb_voting_button"
|
||||
view.attributes.getNamedItem("android:layout_toStartOf").nodeValue =
|
||||
votingButtonId
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw PatchException("Could not find expected xml to modify")
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new controls to the bottom of the YouTube player.
|
||||
*
|
||||
* @param resourceDirectoryName The name of the directory containing the hosting resource.
|
||||
*/
|
||||
fun addBottomControls(resourceDirectoryName: String) {
|
||||
val sourceDocumentEditor = resourceContext.xmlEditor[
|
||||
this::class.java.classLoader.getResourceAsStream(
|
||||
"$resourceDirectoryName/host/layout/$TARGET_RESOURCE_NAME",
|
||||
)!!,
|
||||
]
|
||||
|
||||
val sourceElements = sourceDocumentEditor.file.getElementsByTagName(
|
||||
"android.support.constraint.ConstraintLayout"
|
||||
).item(0).childNodes
|
||||
|
||||
// Copy the patch layout xml into the target layout file.
|
||||
for (index in 1 until sourceElements.length) {
|
||||
val element = sourceElements.item(index).cloneNode(true)
|
||||
|
||||
// If the element has no attributes there's no point to adding it to the destination.
|
||||
if (!element.hasAttributes()) continue
|
||||
|
||||
element.attributes.getNamedItem("yt:layout_constraintRight_toLeftOf").nodeValue = bottomLastLeftOf
|
||||
bottomLastLeftOf = element.attributes.getNamedItem("android:id").nodeValue
|
||||
|
||||
bottomTargetDocumentEditor.file.adoptNode(element)
|
||||
// Elements do not need to be added in the layout order since a layout constraint is used,
|
||||
// but in order is easier to make sense of while debugging.
|
||||
bottomTargetElement.insertBefore(element, bottomInsertBeforeNode)
|
||||
bottomInsertBeforeNode = element
|
||||
}
|
||||
|
||||
sourceDocumentEditor.close()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
arrayOf(
|
||||
"@id/bottom_end_container",
|
||||
"@id/multiview_button",
|
||||
).forEach {
|
||||
bottomTargetDocumentEditor.file.childNodes.findElementByAttributeValue(
|
||||
"android:id",
|
||||
it
|
||||
)?.setAttribute("yt:layout_constraintRight_toLeftOf", bottomLastLeftOf)
|
||||
}
|
||||
|
||||
bottomTargetDocumentEditor.close()
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal object BottomControlsInflateFingerprint : LiteralValueFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL or AccessFlags.SYNTHETIC,
|
||||
returnType = "L",
|
||||
parameters = listOf(),
|
||||
opcodes = listOf(
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT_OBJECT
|
||||
),
|
||||
literalSupplier = { BottomControlsResourcePatch.bottomUiContainerResourceId }
|
||||
)
|
||||
@@ -4,7 +4,10 @@ import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object PlayerControlsVisibilityFingerprint : MethodFingerprint(
|
||||
/**
|
||||
* Resolves to the class found in [PlayerTopControlsInflateFingerprint].
|
||||
*/
|
||||
internal object ControlsOverlayVisibility : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf("Z", "Z")
|
||||
@@ -0,0 +1,17 @@
|
||||
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||
import app.revanced.util.containsWideLiteralInstructionValue
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object OverlayViewInflateFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf("Landroid/view/View;"),
|
||||
customFingerprint = { methodDef, _ ->
|
||||
methodDef.containsWideLiteralInstructionValue(PlayerControlsResourcePatch.fullscreenButton) &&
|
||||
methodDef.containsWideLiteralInstructionValue(PlayerControlsResourcePatch.heatseekerViewstub)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||
|
||||
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
|
||||
internal object PlayerBottomControlsInflateFingerprint : LiteralValueFingerprint(
|
||||
returnType = "Ljava/lang/Object;",
|
||||
parameters = listOf(),
|
||||
literalSupplier = { PlayerControlsResourcePatch.bottomUiContainerResourceId }
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.fingerprint.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object PlayerControlsIntegrationHookFingerprint : MethodFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||
returnType = "V",
|
||||
parameters = listOf("Z"),
|
||||
customFingerprint = { methodDef, classDef ->
|
||||
methodDef.name == "fullscreenButtonVisibilityChanged" &&
|
||||
classDef.type == "Lapp/revanced/integrations/youtube/patches/PlayerControlsPatch;"
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package app.revanced.patches.youtube.misc.playercontrols.fingerprints
|
||||
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||
import app.revanced.util.patch.LiteralValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
internal object PlayerTopControlsInflateFingerprint : LiteralValueFingerprint(
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
returnType = "V",
|
||||
parameters = listOf(),
|
||||
literalSupplier = { PlayerControlsResourcePatch.controlsLayoutStub }
|
||||
)
|
||||
@@ -32,7 +32,7 @@ object PlaybackSpeedButtonPatch : BytecodePatch(emptySet()) {
|
||||
SwitchPreference("revanced_playback_speed_dialog_button"),
|
||||
)
|
||||
|
||||
PlayerControlsBytecodePatch.initializeControl("$SPEED_BUTTON_CLASS_DESCRIPTOR->initializeButton(Landroid/view/View;)V")
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$SPEED_BUTTON_CLASS_DESCRIPTOR->changeVisibility(Z)V")
|
||||
PlayerControlsBytecodePatch.initializeBottomControl(SPEED_BUTTON_CLASS_DESCRIPTOR)
|
||||
PlayerControlsBytecodePatch.injectVisibilityCheckCall(SPEED_BUTTON_CLASS_DESCRIPTOR)
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,12 @@ package app.revanced.patches.youtube.video.speed.button
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.ResourcePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
|
||||
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
|
||||
@Patch(
|
||||
dependencies = [BottomControlsResourcePatch::class],
|
||||
dependencies = [PlayerControlsResourcePatch::class],
|
||||
)
|
||||
internal object PlaybackSpeedButtonResourcePatch : ResourcePatch() {
|
||||
override fun execute(context: ResourceContext) {
|
||||
@@ -20,6 +20,6 @@ internal object PlaybackSpeedButtonResourcePatch : ResourcePatch() {
|
||||
),
|
||||
)
|
||||
|
||||
BottomControlsResourcePatch.addControls("speedbutton")
|
||||
PlayerControlsResourcePatch.addBottomControls("speedbutton")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.Reference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import org.stringtemplate.v4.compiler.Bytecode.instructions
|
||||
|
||||
fun MethodFingerprint.resultOrThrow() = result ?: throw exception
|
||||
|
||||
@@ -73,7 +74,7 @@ fun MutableMethod.injectHideViewCall(
|
||||
* @param resourceName the name of the resource to find the id for.
|
||||
* @return the index of the first instruction with the id of the given resource name, or -1 if not found.
|
||||
* @throws PatchException if the resource cannot be found.
|
||||
* @see [indexOfIdResourceOrThrow]
|
||||
* @see [indexOfIdResourceOrThrow], [indexOfFirstWideLiteralInstructionValueReversed]
|
||||
*/
|
||||
fun Method.indexOfIdResource(resourceName: String): Int {
|
||||
val resourceId = ResourceMappingPatch["id", resourceName]
|
||||
@@ -86,6 +87,7 @@ fun Method.indexOfIdResource(resourceName: String): Int {
|
||||
* Requires [ResourceMappingPatch] as a dependency.
|
||||
*
|
||||
* @throws [PatchException] if the resource is not found, or the method does not contain the resource id literal value.
|
||||
* @see [indexOfIdResource], [indexOfFirstWideLiteralInstructionValueReversedOrThrow]
|
||||
*/
|
||||
fun Method.indexOfIdResourceOrThrow(resourceName: String): Int {
|
||||
val index = indexOfIdResource(resourceName)
|
||||
@@ -120,6 +122,30 @@ fun Method.indexOfFirstWideLiteralInstructionValueOrThrow(literal: Long): Int {
|
||||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the last wide literal instruction with the given value.
|
||||
*
|
||||
* @return the last literal instruction with the value, or -1 if not found.
|
||||
* @see indexOfFirstWideLiteralInstructionValueOrThrow
|
||||
*/
|
||||
fun Method.indexOfFirstWideLiteralInstructionValueReversed(literal: Long) = implementation?.let {
|
||||
it.instructions.indexOfLast { instruction ->
|
||||
(instruction as? WideLiteralInstruction)?.wideLiteral == literal
|
||||
}
|
||||
} ?: -1
|
||||
|
||||
/**
|
||||
* Find the index of the last wide literal instruction with the given value,
|
||||
* or throw an exception if not found.
|
||||
*
|
||||
* @return the last literal instruction with the value, or throws [PatchException] if not found.
|
||||
*/
|
||||
fun Method.indexOfFirstWideLiteralInstructionValueReversedOrThrow(literal: Long): Int {
|
||||
val index = indexOfFirstWideLiteralInstructionValueReversed(literal)
|
||||
if (index < 0) throw PatchException("Could not find literal value: $literal")
|
||||
return index
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the method contains a literal with the given value.
|
||||
*
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package app.revanced.util
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.util.DomFileEditor
|
||||
import app.revanced.util.resource.BaseResource
|
||||
import org.w3c.dom.Attr
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.NodeList
|
||||
import java.io.InputStream
|
||||
@@ -39,6 +42,14 @@ fun Node.doRecursively(action: (Node) -> Unit) {
|
||||
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
|
||||
}
|
||||
|
||||
fun Node.insertFirst(node: Node) {
|
||||
if (hasChildNodes()) {
|
||||
insertBefore(node, firstChild)
|
||||
} else {
|
||||
appendChild(node)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy resources from the current class loader to the resource directory.
|
||||
*
|
||||
@@ -49,7 +60,7 @@ fun ResourceContext.copyResources(
|
||||
sourceResourceDirectory: String,
|
||||
vararg resources: ResourceGroup,
|
||||
) {
|
||||
val targetResourceDirectory = this.get("res")
|
||||
val targetResourceDirectory = this["res", false]
|
||||
|
||||
for (resourceGroup in resources) {
|
||||
resourceGroup.resources.forEach { resource ->
|
||||
@@ -164,3 +175,37 @@ internal fun Node.addResource(
|
||||
}
|
||||
|
||||
internal fun org.w3c.dom.Document.getNode(tagName: String) = this.getElementsByTagName(tagName).item(0)
|
||||
|
||||
internal fun NodeList.findElementByAttributeValue(attributeName: String, value: String): Element? {
|
||||
for (i in 0 until length) {
|
||||
val node = item(i)
|
||||
if (node.nodeType == Node.ELEMENT_NODE) {
|
||||
val element = node as Element
|
||||
|
||||
if (element.getAttribute(attributeName) == value) {
|
||||
return element
|
||||
}
|
||||
|
||||
// Recursively search.
|
||||
val found = element.childNodes.findElementByAttributeValue(attributeName, value)
|
||||
if (found != null) {
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
internal fun NodeList.findElementByAttributeValueOrThrow(attributeName: String, value: String): Element {
|
||||
return findElementByAttributeValue(attributeName, value) ?: throw PatchException("Could not find: $attributeName $value")
|
||||
}
|
||||
|
||||
internal fun Element.copyAttributesFrom(oldContainer: Element) {
|
||||
// Copy attributes from the old element to the new element
|
||||
val attributes = oldContainer.attributes
|
||||
for (i in 0 until attributes.length) {
|
||||
val attr = attributes.item(i) as Attr
|
||||
setAttribute(attr.name, attr.value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,6 +630,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_shorts_save_sound_button_title">Бутон за запазване на аудиото в плейлиста</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_on">Бутонът за Запазване в плейлиста е скрит</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_off">Бутонът за Запазване в плейлиста се показва</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_title">Бутон „Използване на този звук“</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_summary_on">Бутон „Използване на този звук“ е скрит</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_summary_off">Бутон „Използване на този звук“ се показва</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_title">Скриване на предложенията за търсене</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_summary_on">Предложенията за търсене са скрити</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_summary_off">Предложенията за търсене се показват</string>
|
||||
@@ -1131,10 +1134,22 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_slide_to_seek_summary_off">Слайд за превъртане е деактивиран</string>
|
||||
</patch>
|
||||
<patch id="misc.fix.playback.SpoofVideoStreamsPatch">
|
||||
<string name="revanced_spoof_video_streams_screen_title">Подправяне на видео потоци</string>
|
||||
<string name="revanced_spoof_video_streams_screen_summary">Подправете клиентските видео потоци, за да предотвратите проблеми с възпроизвеждането</string>
|
||||
<string name="revanced_spoof_video_streams_title">Подправяне на видео потоци</string>
|
||||
<string name="revanced_spoof_video_streams_summary_on">Видео потоците са подправени</string>
|
||||
<string name="revanced_spoof_video_streams_summary_off">Видео потоците не са подправени\n\nВъзпроизвеждането на видео може да не работи</string>
|
||||
<string name="revanced_spoof_video_streams_user_dialog_message">Деактивирането на тази настройка ще доведе до проблеми с възпроизвеждането на видео.</string>
|
||||
<string name="revanced_spoof_video_streams_client_title">Клиент по подразбиране</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_title">Принудително AVC (H.264)</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Видеокодека е AVC (H.264)</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Видеокодека е VP9 или AV1</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">Вашето устройство няма хардуерно VP9 декодиране и тази настройка винаги е активирана, когато е активно подправяне на клиента</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">Активирането на това може да подобри живота на батерията и да коригира прекъсванията при възпроизвеждане.\n\nAVC има максимална разделителна способност от 1080p и възпроизвеждането на видео ще използва повече интернет данни от VP9 или AV1.</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_title">Cтранични ефекти от подмяната на iOS</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_summary">• Филми или платени видеоклипове може да не се възпроизвеждат\n• Потоците на живо започват отначало\n• Видеоклиповете може да завършват 1 секунда по-рано\n• Няма аудиокодек Opus</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_title">Странични ефекти от подправяне на Android VR</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Липсва менюто за избор аудио\n• Не е налична стабилна сила на звука</string>
|
||||
</patch>
|
||||
<!-- This patch is no longer used and these strings will soon be deleted. -->
|
||||
<patch id="video.hdrbrightness.HDRBrightnessPatch">
|
||||
|
||||
@@ -39,6 +39,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_check_environment_not_same_patching_device">Auf einem anderen Gerät gepatcht</string>
|
||||
<string name="revanced_check_environment_manager_not_expected_installer">Nicht von ReVanced Manager installiert</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time">Vor mehr als 10 Minuten gepatcht</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_days">Vor %s Tagen gepatcht</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time_invalid">APK-Erstellungsdatum ist beschädigt</string>
|
||||
</patch>
|
||||
<patch id="misc.settings.BaseSettingsResourcePatch">
|
||||
@@ -1146,6 +1147,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">Ihr Gerät hat keine VP9-Hardware-Dekodierung, und diese Einstellung ist immer aktiviert, wenn Client-Spoofing aktiviert ist</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">Das Aktivieren kann die Akkulaufzeit verbessern und die Wiedergabe-Stutting beheben.\n\nAVC hat eine maximale Auflösung von 1080p, und die Videowiedergabe wird mehr Internet-Daten als VP9 oder AV1 verwenden.</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_title">iOS Spoofing Nebeneffekte</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_summary">• Filme oder bezahlte Videos werden möglicherweise nicht abgespielt\n• Livestreams starten von Anfang an\n• Videos enden möglicherweise 1 Sekunde früher\n• kein Opus-Audiocodec</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_title">Android VR Spoofing Nebeneffekte</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Audio Track Menü fehlt\n• Stabile Lautstärke ist nicht verfügbar</string>
|
||||
</patch>
|
||||
|
||||
@@ -100,7 +100,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_debug_toast_on_error_title">Mostrar brindis en error ReVanced</string>
|
||||
<string name="revanced_debug_toast_on_error_summary_on">Toast mostrado si ocurre un error</string>
|
||||
<string name="revanced_debug_toast_on_error_summary_off">Toast no se muestra si ocurre un error</string>
|
||||
<string name="revanced_debug_toast_on_error_user_dialog_message">Desactivar los brindis de errores oculta todas las notificaciones de error ReVanced\n\nNo se le notificará de ningún evento inesperado.</string>
|
||||
<string name="revanced_debug_toast_on_error_user_dialog_message">Desactivar los avisos (toasts) de errores oculta todas las notificaciones de error ReVanced\n\nNo se le notificará de ningún evento inesperado.</string>
|
||||
</patch>
|
||||
<patch id="layout.hide.general.HideLayoutComponentsPatch">
|
||||
<string name="revanced_disable_like_subscribe_glow_title">Desactivar el brillo del botón de like / suscripción</string>
|
||||
@@ -199,8 +199,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_image_shelf_summary_on">El estante de imágenes está oculto</string>
|
||||
<string name="revanced_hide_image_shelf_summary_off">El estante de imágenes se muestra</string>
|
||||
<string name="revanced_hide_latest_posts_ads_title">Ocultar últimos mensajes</string>
|
||||
<string name="revanced_hide_latest_posts_ads_summary_on">Los últimos mensajes están ocultos</string>
|
||||
<string name="revanced_hide_latest_posts_ads_summary_off">Se muestran los últimos mensajes</string>
|
||||
<string name="revanced_hide_latest_posts_ads_summary_on">Las últimas publicaciones están ocultas</string>
|
||||
<string name="revanced_hide_latest_posts_ads_summary_off">Se muestran las últimas publicaciones</string>
|
||||
<string name="revanced_hide_mix_playlists_title">Ocultar listas de mezcla</string>
|
||||
<string name="revanced_hide_mix_playlists_summary_on">Las listas de reproducción mixtas están ocultas</string>
|
||||
<string name="revanced_hide_mix_playlists_summary_off">Mezclar listas de reproducción son mostradas</string>
|
||||
@@ -258,11 +258,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_keyword_content_about_summary">Los resultados de inicio/suscripción/búsqueda se filtran para ocultar el contenido que coincide con las frases de palabras clave\n\nLimitaciones\n• Los cortos no se pueden ocultar con el nombre del canal\n• Algunos componentes de la interfaz pueden no estar ocultos\n• Buscar una palabra clave no puede mostrar resultados</string>
|
||||
<string name="revanced_hide_keyword_content_about_whole_words_title">Coincidir palabras completas</string>
|
||||
<!-- Translations _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
|
||||
<string name="revanced_hide_keyword_content_about_whole_words_summary">Rodear una palabra clave/frase con comillas dobles evitará las coincidencias parciales de títulos de vídeo y nombres de canales<br><br>Por ejemplo,<br><b>\"ia\"</b> ocultará el vídeo: <b>¿Cómo funciona la IA?</b><br>pero no ocultará: <b>¿Qué significa uso justo?</b></string>
|
||||
<string name="revanced_hide_keyword_content_about_whole_words_summary">Rodear una palabra clave/frase con comillas dobles evitará las coincidencias parciales de títulos de vídeo y nombres de canales<br><br>Por ejemplo,<br><b>\"ia\"</b> ocultará el vídeo: <b>¿Cómo funciona la AI?</b><br>pero no ocultará: <b>¿Quieres aprender a bailar?</b></string>
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
<string name="revanced_hide_keyword_toast_invalid_common">No se puede usar la palabra clave: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_common_whole_word_required">Añadir comillas para usar palabra clave: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_conflicting">La palabra clave tiene declaraciones en conflicto: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_conflicting">La palabra clave tiene declaraciones conflictivas: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_length">La palabra clave es demasiado corta y requiere comillas: %s</string>
|
||||
<string name="revanced_hide_keyword_toast_invalid_broad">Palabra clave ocultará todos los vídeos: %s</string>
|
||||
</patch>
|
||||
@@ -274,7 +274,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_fullscreen_ads_summary_on">Los anuncios a pantalla completa están ocultos\n\nEsta función solo está disponible para dispositivos más antiguos</string>
|
||||
<string name="revanced_hide_fullscreen_ads_summary_off">Se muestran anuncios a pantalla completa</string>
|
||||
<string name="revanced_hide_buttoned_ads_title">Ocultar anuncios botonados</string>
|
||||
<string name="revanced_hide_buttoned_ads_summary_on">Los anuncios bloqueados están ocultos</string>
|
||||
<string name="revanced_hide_buttoned_ads_summary_on">Los anuncios botonados están ocultos</string>
|
||||
<string name="revanced_hide_buttoned_ads_summary_off">Se muestran anuncios botonados</string>
|
||||
<string name="revanced_hide_paid_promotion_label_title">Ocultar etiqueta de promoción de pago</string>
|
||||
<string name="revanced_hide_paid_promotion_label_summary_on">Etiqueta de promoción pagada está oculta</string>
|
||||
@@ -307,7 +307,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_get_premium_summary_off">Se muestran las promociones de YouTube Premium en el reproductor de vídeo</string>
|
||||
</patch>
|
||||
<patch id="ad.video.VideoAdsPatch">
|
||||
<string name="revanced_hide_video_ads_title">Ocultar video anuncios</string>
|
||||
<string name="revanced_hide_video_ads_title">Ocultar anuncios de video</string>
|
||||
<string name="revanced_hide_video_ads_summary_on">Los anuncios de vídeo están ocultos</string>
|
||||
<string name="revanced_hide_video_ads_summary_off">Los anuncios de vídeo se muestran</string>
|
||||
</patch>
|
||||
@@ -328,7 +328,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">Esto no pasa por alto la restricción de edad, sino que simplemente la acepta automáticamente.</string>
|
||||
</patch>
|
||||
<patch id="interaction.downloads.DownloadsResourcePatch">
|
||||
<string name="revanced_external_downloader_screen_title">Descarga externa</string>
|
||||
<string name="revanced_external_downloader_screen_title">Descargas externa</string>
|
||||
<string name="revanced_external_downloader_screen_summary">Configuración para el uso de un descargador externo</string>
|
||||
<string name="revanced_external_downloader_title">Mostrar botón externo de descarga</string>
|
||||
<string name="revanced_external_downloader_summary_on">Botón de descarga mostrado en el reproductor</string>
|
||||
@@ -442,8 +442,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_navigation_buttons_screen_summary">Ocultar o cambiar botones en la barra de navegación</string>
|
||||
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
|
||||
<string name="revanced_hide_home_button_title">Ocultar Principal</string>
|
||||
<string name="revanced_hide_home_button_summary_on">El botón de principal está oculto</string>
|
||||
<string name="revanced_hide_home_button_summary_off">Se muestra el botón de principal</string>
|
||||
<string name="revanced_hide_home_button_summary_on">El botón de inicio está oculto</string>
|
||||
<string name="revanced_hide_home_button_summary_off">El botón de inicio es visible</string>
|
||||
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
|
||||
<string name="revanced_hide_shorts_button_title">Ocultar Shorts</string>
|
||||
<string name="revanced_hide_shorts_button_summary_on">El botón de Shorts está oculto</string>
|
||||
@@ -928,7 +928,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_reset_color">Reiniciar color</string>
|
||||
<string name="revanced_sb_reset">Restablecer</string>
|
||||
<string name="revanced_sb_about">Acerca de</string>
|
||||
<string name="revanced_sb_about_api_sum">Los datos son proporcionados por la API de SponsorBlock. Pulsa aquí para aprender más y ver las descargas de otras plataformas</string>
|
||||
<string name="revanced_sb_about_api_sum">Los datos son proporcionados por la API de SponsorBlock. Pulsa aquí para aprender más y ver las descargas para otras plataformas</string>
|
||||
</patch>
|
||||
<patch id="layout.spoofappversion.SpoofAppVersionPatch">
|
||||
<string name="revanced_spoof_app_version_title">Versión de la aplicación Spoof</string>
|
||||
@@ -1164,8 +1164,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_block_audio_ads_summary_off">Anuncios de audio desbloqueados</string>
|
||||
</patch>
|
||||
<patch id="ad.embedded.EmbeddedAdsPatch">
|
||||
<string name="revanced_embedded_ads_service_unavailable">%s no está disponible. Los anuncios pueden mostrarse. Intenta cambiar a otro servicio de bloque de anuncios en la configuración.</string>
|
||||
<string name="revanced_embedded_ads_service_failed">El servidor %s devolvió un error. Los anuncios pueden mostrar. Intente cambiar a otro servicio de bloque de anuncios en la configuración.</string>
|
||||
<string name="revanced_embedded_ads_service_unavailable">%s no está disponible. Los anuncios pueden mostrarse. Intenta cambiar a otro servicio de bloqueo de anuncios en la configuración.</string>
|
||||
<string name="revanced_embedded_ads_service_failed">El servidor %s devolvió un error. Los anuncios pueden mostrar. Intente cambiar a otro servicio de bloqueo de anuncios en la configuración.</string>
|
||||
<string name="revanced_block_embedded_ads_title">Bloquear anuncios de vídeo incrustados</string>
|
||||
<string name="revanced_block_embedded_ads_entry_1">Desactivado</string>
|
||||
<string name="revanced_block_embedded_ads_entry_2">Proxy luminoso</string>
|
||||
|
||||
@@ -1146,7 +1146,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">Níl díchódú crua-earraí VP9 ar do ghléas, agus bíonn an socrú seo ar siúl i gcónaí nuair atá spoofing Cliant cumasaithe</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">Má dhéantar é seo a chumasú, d\'fhéadfadh sé go bhfeabhsófaí saol na gceallraí agus go n-athshocraigh sé stopáil athsheinm.\n\nTá uas-taifeach 1080p ag AVC, agus úsáidfidh athsheinm físe níos mó sonraí idirlín ná mar a úsáideann VP9 nó AV1.</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_title">Fo-iarsmaí spoofing iOS</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_summary">• Ní féidir le scannáin nó físeáin íoctha a sheinm\n• Tosaíonn sruthanna beo ón tús\n• Seans go gcríochnóidh físeáin 1 soicind go luath\n• Gan codec fuaime opus</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_title">Fo-iarsmaí spoofing Android VR</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Tá roghchlár rian fuaime in easnamh\n• Níl an toirt cobhsaí ar fáil</string>
|
||||
</patch>
|
||||
<!-- This patch is no longer used and these strings will soon be deleted. -->
|
||||
<patch id="video.hdrbrightness.HDRBrightnessPatch">
|
||||
|
||||
@@ -630,6 +630,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_shorts_save_sound_button_title">Hang mentése a lejátszási listára gomb elrejtése</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_on">A hang mentése a lejátszási listára gomb el van rejtve</string>
|
||||
<string name="revanced_hide_shorts_save_sound_button_summary_off">A hang mentése a lejátszási listára gomb megjelenik</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_title">\'Használja ezt a hang gombot\' elrejtése</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_summary_on">\'Használja ezt a hang gombot\' elrejtve</string>
|
||||
<string name="revanced_hide_shorts_use_this_sound_button_summary_off">\'Használja ezt a hang gombot\' látszik</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_title">Keresési javaslatok elrejtése</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_summary_on">A keresési javaslatok el vannak rejtve</string>
|
||||
<string name="revanced_hide_shorts_search_suggestions_summary_off">A keresési javaslatok megjelennek</string>
|
||||
@@ -1131,10 +1134,22 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_slide_to_seek_summary_off">A csúsztatás a kereséshez nincs engedélyezve</string>
|
||||
</patch>
|
||||
<patch id="misc.fix.playback.SpoofVideoStreamsPatch">
|
||||
<string name="revanced_spoof_video_streams_screen_title">Hamis videó stream</string>
|
||||
<string name="revanced_spoof_video_streams_screen_summary">Hamisítsa meg az ügyfél videó streamet a lejátszási problémák elkerülése érdekében</string>
|
||||
<string name="revanced_spoof_video_streams_title">Hamis videó stream</string>
|
||||
<string name="revanced_spoof_video_streams_summary_on">A videó stream hamisítva</string>
|
||||
<string name="revanced_spoof_video_streams_summary_off">A videó stream nincs hamisítva\n\nLehet, hogy a videólejátszás nem működik</string>
|
||||
<string name="revanced_spoof_video_streams_user_dialog_message">A beállítás kikapcsolása videolejátszási problémákat okozhat.</string>
|
||||
<string name="revanced_spoof_video_streams_client_title">Alapértelmezett kliens</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_title">AVC (H.264) kényszerítése</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">A videokodek AVC (H.264)</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">A videokodek VP9 vagy AV1</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">Eszközén nincs VP9 hardveres dekódolás, és ez a beállítás mindig be van kapcsolva, ha az ügyfélhamisítás engedélyezve van</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">Ennek engedélyezése javíthatja az akkumulátor élettartamát, és kijavíthatja a lejátszás akadozását.\n\nAz AVC maximális felbontása 1080p, és a videolejátszás több internetadatot használ, mint a VP9 vagy az AV1.</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_title">iOS hamisítási mellékhatások</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_summary">• Előfordulhat, hogy a filmeket és a fizetős videókat nem lehet lejátszani\n• Az élő közvetítések elölről kezdődnek.\n• A videók 1 másodperccel korábban véget érhetnek\n• Nincs opus audiokodek</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_title">Android VR-hamisítási mellékhatások</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Hiányzik a hangsáv menü\n• A stabil hangerő nem érhető el</string>
|
||||
</patch>
|
||||
<!-- This patch is no longer used and these strings will soon be deleted. -->
|
||||
<patch id="video.hdrbrightness.HDRBrightnessPatch">
|
||||
|
||||
@@ -87,7 +87,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
</patch>
|
||||
<patch id="misc.debugging.DebuggingPatch">
|
||||
<string name="revanced_debug_screen_title">Debugging</string>
|
||||
<string name="revanced_debug_screen_summary">Mengaktifkan atau menonaktifkan opsi debugging</string>
|
||||
<string name="revanced_debug_screen_summary">Menyalakan atau mematikan opsi debugging</string>
|
||||
<string name="revanced_debug_title">Catatan debug</string>
|
||||
<string name="revanced_debug_summary_on">Log debug diaktifkan</string>
|
||||
<string name="revanced_debug_summary_off">Log debug dinonaktifkan</string>
|
||||
@@ -109,7 +109,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_gray_separator_title">Sembunyikan pemisah abu-abu</string>
|
||||
<string name="revanced_hide_gray_separator_summary_on">Pemisah abu-abu disembunyikan</string>
|
||||
<string name="revanced_hide_gray_separator_summary_off">Pemisah abu-abu ditampilkan</string>
|
||||
<string name="revanced_hide_channel_watermark_title">Sembunyikan watermark saluran</string>
|
||||
<string name="revanced_hide_channel_watermark_title">Sembunyikan tanda air saluran</string>
|
||||
<string name="revanced_hide_channel_watermark_summary_on">Tanda air disembunyikan</string>
|
||||
<string name="revanced_hide_channel_watermark_summary_off">Tanda air ditampilkan</string>
|
||||
<string name="revanced_hide_horizontal_shelves_title">Sembunyikan rak mendatar</string>
|
||||
@@ -289,15 +289,15 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_shopping_links_summary_on">Tautan belanja disembunyikan</string>
|
||||
<string name="revanced_hide_shopping_links_summary_off">Tautan belanja ditampilkan</string>
|
||||
<!-- 'Visit store' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_visit_store_button_title">Sembunyikan \'Lihat toko\' di laman saluran</string>
|
||||
<string name="revanced_hide_visit_store_button_title">Sembunyikan tombol \'Lihat toko\' di laman saluran</string>
|
||||
<string name="revanced_hide_visit_store_button_summary_on">Tombol disembunyikan</string>
|
||||
<string name="revanced_hide_visit_store_button_summary_off">Tombol ditampilkan</string>
|
||||
<string name="revanced_hide_web_search_results_title">Sembunyikan hasil pencarian web</string>
|
||||
<string name="revanced_hide_web_search_results_summary_on">Hasil pencarian web disembunyikan</string>
|
||||
<string name="revanced_hide_web_search_results_summary_off">Hasil pencarian web ditampilkan</string>
|
||||
<string name="revanced_hide_merchandise_banners_title">Sembunyikan banner merchandise</string>
|
||||
<string name="revanced_hide_merchandise_banners_title">Sembunyikan spanduk barang dagangan</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_on">Banner merchandise disembunyikan</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">Banner merchandise ditampilkan</string>
|
||||
<string name="revanced_hide_merchandise_banners_summary_off">Spanduk barang dagangan ditampilkan</string>
|
||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||
<string name="revanced_hide_fullscreen_ads_feature_not_available_toast">Sembunyikan iklan layar penuh hanya berfungsi pada perangkat lama</string>
|
||||
</patch>
|
||||
@@ -409,9 +409,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_download_button_summary_on">Tombol download disembunyikan</string>
|
||||
<string name="revanced_hide_download_button_summary_off">Tombol download ditampilkan</string>
|
||||
<!-- 'Thanks' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_thanks_button_title">Sembunyikan Thanks</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">Tombol thanks disembunyikan</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Tombol thanks ditampilkan</string>
|
||||
<string name="revanced_hide_thanks_button_title">Sembunyikan Terima kasih</string>
|
||||
<string name="revanced_hide_thanks_button_summary_on">Tombol terima kasih disembunyikan</string>
|
||||
<string name="revanced_hide_thanks_button_summary_off">Tombol terima kasih ditampilkan</string>
|
||||
<!-- 'Clip' should be translated with the same localized wording that YouTube displays. -->
|
||||
<string name="revanced_hide_clip_button_title">Sembunyikan Klip</string>
|
||||
<string name="revanced_hide_clip_button_summary_on">Tombol klip disembunyikan</string>
|
||||
@@ -535,8 +535,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_comments_preview_comment_summary_on">Komentar pratinjau disembunyikan</string>
|
||||
<string name="revanced_hide_comments_preview_comment_summary_off">Komentar pratinjau ditampilkan</string>
|
||||
<string name="revanced_hide_comments_thanks_button_title">Sembunyikan \'terima kasih\'</string>
|
||||
<string name="revanced_hide_comments_thanks_button_summary_on">Tmbl terima kasih disembunyikan</string>
|
||||
<string name="revanced_hide_comments_thanks_button_summary_off">Tmbl terima kasih ditampilkan</string>
|
||||
<string name="revanced_hide_comments_thanks_button_summary_on">Tombol terima kasih disembunyikan</string>
|
||||
<string name="revanced_hide_comments_thanks_button_summary_off">Tombol terima kasih ditampilkan</string>
|
||||
<string name="revanced_hide_comments_timestamp_and_emoji_buttons_title">Sembunyikan timestamp dan tombol emoji</string>
|
||||
<string name="revanced_hide_comments_timestamp_and_emoji_buttons_summary_on">Tombol timestamp dan emoji disembunyikan</string>
|
||||
<string name="revanced_hide_comments_timestamp_and_emoji_buttons_summary_off">Tombol timestamp dan emoji ditampilkan</string>
|
||||
@@ -872,7 +872,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_submit_failed_duplicate">Tidak dapat mengirim segmen.\nSudah ada</string>
|
||||
<string name="revanced_sb_submit_succeeded">Segmen berhasil terkirim</string>
|
||||
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
|
||||
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock sementara tidak tersedia (API timed out).</string>
|
||||
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock tidak tersedia (API kehabisan waktu)</string>
|
||||
<string name="revanced_sb_sponsorblock_connection_failure_status">SponsorBlock sementara tidak tersedia (status %d)</string>
|
||||
<string name="revanced_sb_sponsorblock_connection_failure_generic">SponsorBlock sementara tidak tersedia</string>
|
||||
<string name="revanced_sb_vote_failed_timeout">Tidak dapat memilih segmen (API timed out)</string>
|
||||
@@ -894,7 +894,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_sb_new_segment_confirm_title">Apakah waktunya benar?</string>
|
||||
<string name="revanced_sb_new_segment_confirm_content">Segmen dari\n\n%1$s\nke\n%2$s\n\n(%3$s)\n\nSiap dikirim?</string>
|
||||
<string name="revanced_sb_new_segment_start_is_before_end">Awal harus sebelum akhir</string>
|
||||
<string name="revanced_sb_new_segment_mark_locations_first">Tandai terlebih dahulu dua lokasi di kotak waktu</string>
|
||||
<string name="revanced_sb_new_segment_mark_locations_first">Tandai dua lokasi pada bilah waktu terlebih dahulu</string>
|
||||
<string name="revanced_sb_new_segment_preview_segment_first">Pratinjau segmen, dan memastikan segmen dilewati dengan lancar</string>
|
||||
<string name="revanced_sb_new_segment_edit_by_hand_title">Atur pengaturan tempo segmen secara manual</string>
|
||||
<string name="revanced_sb_new_segment_edit_by_hand_content">Apakah Anda ingin mengubah tempo untuk awal atau akhir dari segmen?</string>
|
||||
|
||||
@@ -35,8 +35,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<patch id="misc.checks.BaseCheckEnvironmentPatch">
|
||||
<string name="revanced_check_environment_failed_title">확인에 실패함</string>
|
||||
<string name="revanced_check_environment_dialog_open_official_source_button">공식 홈페이지 열기</string>
|
||||
<string name="revanced_check_environment_dialog_ignore_button">무시</string>
|
||||
<string name="revanced_check_environment_failed_message"><h5>이 앱은 사용자가 패치하지 않은 것 같습니다.</h5><br>이 앱은 제대로 작동하지 않을 수 있으며, <b>사용 시 해롭거나 심지어 위험할 수도 있습니다</b>.<br><br>이러한 확인은 이 앱이 사전 패치되었거나 다른 사람에게서 얻은 것임을 의미합니다:<br><br><small>%1$s</small><br>검증되고 안전한 앱을 사용하고 있는지 확인하려면 <b>이 앱을 제거하고 직접 패치하는 것</b>을 강력히 권장합니다.<p><br>이 경고를 무시하면 두 번만 표시됩니다.</string>
|
||||
<string name="revanced_check_environment_dialog_ignore_button">닫기</string>
|
||||
<string name="revanced_check_environment_failed_message"><h5>이 앱은 사용자가 패치하지 않은 것 같습니다.</h5><br>이 앱은 제대로 작동하지 않을 수 있으며, <b>사용 시 해롭거나 심지어 위험할 수도 있습니다</b>.<br><br>이러한 확인은 이 앱이 사전에 패치되었거나 다른 사람으로부터 받은 것임을 의미합니다:<br><br><small>%1$s</small><br>검증되고 안전한 앱을 사용하고 있는지 확인하려면 <b>이 앱을 제거하고 직접 패치하는 것</b>을 강력히 권장합니다.<p><br>이 경고는 두 번만 표시됩니다.</string>
|
||||
<string name="revanced_check_environment_not_same_patching_device">다른 기기에서 패치됨</string>
|
||||
<string name="revanced_check_environment_manager_not_expected_installer">ReVanced Manager에 의해 설치되지 않음</string>
|
||||
<string name="revanced_check_environment_not_near_patch_time">10분 이상 전에 패치됨</string>
|
||||
@@ -459,8 +459,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_subscriptions_button_summary_off">구독 버튼이 표시됩니다</string>
|
||||
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
|
||||
<string name="revanced_switch_create_with_notifications_button_title">만들기 버튼과 알림 버튼 위치 교환하기</string>
|
||||
<string name="revanced_switch_create_with_notifications_button_summary_on">만들기 버튼과 알림 버튼의 위치를 교환합니다\n\n알려진 문제점:\n• 이 설정을 비활성화하면 서버에서 광고 필터에 등록되지 않은 광고(Shorts 광고)가 로드됩니다\n• 이 설정을 활성화하면 일부 광고가 강제로 숨겨집니다 (동영상 광고, 일반 레이아웃 광고)\n• 광고 설정에 있는 일부 설정들을 비활성화하려면 이 설정도 비활성화해야 합니다</string>
|
||||
<string name="revanced_switch_create_with_notifications_button_summary_off">만들기 버튼과 알림 버튼을 위치를 교환하지 않습니다\n\n알려진 문제점:\n• 이 설정을 비활성화하면 서버에서 광고 필터에 등록되지 않은 광고(Shorts 광고)가 로드됩니다\n• 이 설정을 활성화하면 일부 광고가 강제로 숨겨집니다 (동영상 광고, 일반 레이아웃 광고)\n• 광고 설정에 있는 일부 설정들을 비활성화하려면 이 설정도 비활성화해야 합니다</string>
|
||||
<string name="revanced_switch_create_with_notifications_button_summary_on">만들기 버튼과 알림 버튼의 위치를 교환합니다\n\n알려진 문제점:\n• 동영상 광고가 강제로 숨겨집니다</string>
|
||||
<string name="revanced_switch_create_with_notifications_button_summary_off">만들기 버튼과 알림 버튼의 위치를 교환하지 않습니다\n\n알려진 문제점:\n• 서버에서 더 많은 광고가 로드될 수 있습니다\n• Shorts 광고가 더 이상 숨겨지지 않습니다</string>
|
||||
<string name="revanced_hide_navigation_button_labels_title">하단바 버튼 라벨 숨기기</string>
|
||||
<string name="revanced_hide_navigation_button_labels_summary_on">라벨이 숨겨집니다</string>
|
||||
<string name="revanced_hide_navigation_button_labels_summary_off">라벨이 표시됩니다</string>
|
||||
|
||||
@@ -1147,9 +1147,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">На вашем устройстве нет аппаратного декодирования VP9, и эта настройка всегда включена при активной подмене клиента</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">Включение данной настройки может улучшить время работы батареи и исправить задержки воспроизведения.\n\nAVC имеет максимальное разрешение 1080p, воспроизведение видео будет использовать больше интернет данных в сравнении с VP9 или AV1.</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_title">Побочные эффекты подмены на iOS</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_summary">• Фильмы или платные видео не могут проигрывать\n• Livestreams начало с начала\n• Видео могут закончиться 1 секунду рано\n• Нет аудио кодека</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_summary">• Фильмы или платные видео могут не воспроизводиться\n• Прямые трансляции начинаются с самого начала\n• Видео может закончиться на 1 секунду раньше\n• Отсутствует аудиокодек opus</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_title">Побочные эффекты подмены на Android VR</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Отсутствует меню аудио дорожки\n• Стабильная громкость недоступна</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Пункт меню \"Звуковая дорожка\" отсутствует\n• Пункт меню \"Постоянный уровень громкости\" недоступен</string>
|
||||
</patch>
|
||||
<!-- This patch is no longer used and these strings will soon be deleted. -->
|
||||
<patch id="video.hdrbrightness.HDRBrightnessPatch">
|
||||
|
||||
@@ -669,9 +669,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_shorts_full_video_link_label_title">Sakrij oznaku linka Shorts videa</string>
|
||||
<string name="revanced_hide_shorts_full_video_link_label_summary_on">Oznaka linka Shorts videa je skrivena</string>
|
||||
<string name="revanced_hide_shorts_full_video_link_label_summary_off">Oznaka linka Shorts videa je prikazana</string>
|
||||
<string name="revanced_hide_shorts_sound_button_title">Sakrij dugme za zvuk</string>
|
||||
<string name="revanced_hide_shorts_sound_button_summary_on">Dugme za zvuk je skriveno</string>
|
||||
<string name="revanced_hide_shorts_sound_button_summary_off">Dugme za zvuk je prikazano</string>
|
||||
<string name="revanced_hide_shorts_sound_button_title">Sakrij dugme „Zvuk”</string>
|
||||
<string name="revanced_hide_shorts_sound_button_summary_on">Dugme „Zvuk” je skriveno</string>
|
||||
<string name="revanced_hide_shorts_sound_button_summary_off">Dugme „Zvuk” je prikazano</string>
|
||||
<string name="revanced_hide_shorts_navigation_bar_title">Sakrij traku za navigaciju</string>
|
||||
<string name="revanced_hide_shorts_navigation_bar_summary_on">Traka za navigaciju je skrivena</string>
|
||||
<string name="revanced_hide_shorts_navigation_bar_summary_off">Traka za navigaciju je prikazana</string>
|
||||
@@ -1140,7 +1140,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_video_streams_title">Lažirani video strimovi</string>
|
||||
<string name="revanced_spoof_video_streams_summary_on">Video strimovi su lažirani</string>
|
||||
<string name="revanced_spoof_video_streams_summary_off">Video strimovi nisu lažirani\n\nReprodukcija videa možda neće raditi</string>
|
||||
<string name="revanced_spoof_video_streams_user_dialog_message">Isključivanje ovog podešavanja možda će izazvati probleme sa reprodukcijom videa.</string>
|
||||
<string name="revanced_spoof_video_streams_user_dialog_message">Isključivanje ove opcije će možda izazvati probleme sa reprodukcijom videa.</string>
|
||||
<string name="revanced_spoof_video_streams_client_title">Podrazumevani klijent</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_title">Prisili AVC (H.264)</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Video kodek je AVC (H.264)</string>
|
||||
@@ -1148,7 +1148,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">Vaš uređaj nema VP9 hardversko dekodiranje, i ova opcija je uvek uključena kada je omogućeno lažiranje klijenta</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">Ako ovo omogućite, možda će se produžiti trajanje baterije i popraviti zastoj pri reprodukciji.\n\nAVC ima maksimalnu rezoluciju od 1080p, a reprodukcija videa će koristiti više internet podataka nego VP9 ili AV1.</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_title">Neželjeni efekti lažiranja na iOS</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_summary">• Filmovi ili plaćeni videi se možda neće puštati\n• Strimovi uživo počinju od početka\n• Videi će se možda završiti 1 sekundu ranije\n• Nema audio kodeka opus</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_title">Neželjeni efekti lažiranja na Android VR</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Meni „Audio snimak” nedostaje\n• Opcija „Ujednačena jačina zvuka” nije dostupna</string>
|
||||
</patch>
|
||||
<!-- This patch is no longer used and these strings will soon be deleted. -->
|
||||
<patch id="video.hdrbrightness.HDRBrightnessPatch">
|
||||
|
||||
@@ -669,9 +669,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_hide_shorts_full_video_link_label_title">Сакриј ознаку линка Shorts видеа</string>
|
||||
<string name="revanced_hide_shorts_full_video_link_label_summary_on">Ознака линка Shorts видеа је скривена</string>
|
||||
<string name="revanced_hide_shorts_full_video_link_label_summary_off">Ознака линка Shorts видеа је приказана</string>
|
||||
<string name="revanced_hide_shorts_sound_button_title">Сакриј дугме за звук</string>
|
||||
<string name="revanced_hide_shorts_sound_button_summary_on">Дугме за звук је скривено</string>
|
||||
<string name="revanced_hide_shorts_sound_button_summary_off">Дугме за звук је приказано</string>
|
||||
<string name="revanced_hide_shorts_sound_button_title">Сакриј дугме „Звук”</string>
|
||||
<string name="revanced_hide_shorts_sound_button_summary_on">Дугме „Звук” је скривено</string>
|
||||
<string name="revanced_hide_shorts_sound_button_summary_off">Дугме „Звук” је приказано</string>
|
||||
<string name="revanced_hide_shorts_navigation_bar_title">Сакриј траку за навигацију</string>
|
||||
<string name="revanced_hide_shorts_navigation_bar_summary_on">Трака за навигацију је скривена</string>
|
||||
<string name="revanced_hide_shorts_navigation_bar_summary_off">Трака за навигацију је приказана</string>
|
||||
@@ -1148,7 +1148,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">Ваш уређај нема VP9 хардверско декодирање, и ова опција је увек укључена када је омогућено лажирање клијента</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">Ако ово омогућите, можда ће се продужити трајање батерије и поправити застој при репродукцији.\n\nAVC има максималну резолуцију од 1080p, а репродукција видеа ће користити више интернет података него VP9 или AV1.</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_title">Нежељени ефекти лажирања на iOS</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_summary">• Филмови или плаћени видеи се можда неће пуштати\n• Стримови уживо почињу од почетка\n• Видеи ће се можда завршити 1 секунду раније\n• Нема аудио кодека opus</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_title">Нежељени ефекти лажирања на Android VR</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Мени „Аудио снимак” недостаје\n• Опција „Уједначена јачина звука” није доступна</string>
|
||||
</patch>
|
||||
<!-- This patch is no longer used and these strings will soon be deleted. -->
|
||||
<patch id="video.hdrbrightness.HDRBrightnessPatch">
|
||||
|
||||
@@ -1145,9 +1145,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Примусово увімкнено відеокодек VP9 або AV1</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">Ваш пристрій не має апаратного декодування VP9, тому це налаштування завжди ввімкнено, коли ввімкнено підробку відеопотоків</string>
|
||||
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">Увімкнення цієї опції може збільшити час роботи від акумулятора та виправити затримки відтворення.\n\nAVC має максимальну роздільну здатність 1080p, а відтворення відео використовуватиме більше інтернет-даних, ніж на кодеках VP9 або AV1.</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_title">Побічні ефекти підміни iOS:</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_title">Побічні ефекти підробки iOS:</string>
|
||||
<string name="revanced_spoof_video_streams_about_ios_summary">• Фільми чи платні відео можуть не відтворюватися\n• Прямі трансляції починаються з початку\n• Відео можуть закінчуватися на 1 секунду раніше\n• Відсутній аудіокодек Opus</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_title">Побічні ефекти підміни Android VR:</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_title">Побічні ефекти підробки Android VR:</string>
|
||||
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Меню звукової доріжки відсутнє\n• Меню стабілізації гучності недоступне</string>
|
||||
</patch>
|
||||
<!-- This patch is no longer used and these strings will soon be deleted. -->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/content_copy/materialsymbolsoutlined/content_copy_wght200gradN25_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/content_copy/materialsymbolsoutlined/content_copy_wght200gradN25_24px.xml
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/schedule/materialsymbolsoutlined/schedule_wght300_24px.xml
|
||||
This icon is the result of a combination of "content copy" and "schedule" icons.
|
||||
Changes made: This icon is the result of a combination of "content copy" and "schedule" icons.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:yt="http://schemas.android.com/apk/res-auto" android:id="@+id/youtube_controls_bottom_ui_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layoutDirection="ltr">
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView android:id="@+id/revanced_copy_video_url_timestamp_button" android:paddingLeft="12dp" android:paddingTop="22dp" android:paddingRight="12dp" android:paddingBottom="16dp" android:longClickable="false" android:layout_width="60dp" android:layout_height="60dp" android:src="@drawable/revanced_yt_copy_timestamp" android:scaleType="center" yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" style="@style/YouTubePlayerButton"/>
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView android:id="@+id/revanced_copy_video_url_button" android:paddingLeft="12dp" android:paddingTop="22dp" android:paddingRight="12dp" android:paddingBottom="16dp" android:longClickable="false" android:layout_width="60dp" android:layout_height="60dp" android:src="@drawable/revanced_yt_copy" android:scaleType="center" yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" style="@style/YouTubePlayerButton"/>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:yt="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/youtube_controls_bottom_ui_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layoutDirection="ltr">
|
||||
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView
|
||||
android:id="@+id/revanced_copy_video_url_timestamp_button"
|
||||
style="@style/YouTubePlayerButton"
|
||||
android:layout_width="48.0dip"
|
||||
android:layout_height="60.0dip"
|
||||
android:paddingTop="6.0dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:longClickable="false"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/revanced_yt_copy_timestamp"
|
||||
yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container"
|
||||
yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" />
|
||||
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView
|
||||
android:id="@+id/revanced_copy_video_url_button"
|
||||
style="@style/YouTubePlayerButton"
|
||||
android:layout_width="48.0dip"
|
||||
android:layout_height="60.0dip"
|
||||
android:paddingTop="6.0dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:longClickable="false"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/revanced_yt_copy"
|
||||
yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container"
|
||||
yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/download/materialsymbolsoutlined/download_wght200gradN25_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:yt="http://schemas.android.com/apk/res-auto" android:id="@+id/youtube_controls_bottom_ui_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layoutDirection="ltr">
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView android:id="@+id/revanced_external_download_button" android:paddingLeft="12dp" android:paddingTop="22dp" android:paddingRight="12dp" android:paddingBottom="16dp" android:longClickable="false" android:layout_width="60dp" android:layout_height="60dp" android:src="@drawable/revanced_yt_download_button" android:scaleType="center" yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" style="@style/YouTubePlayerButton"/>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:yt="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/youtube_controls_bottom_ui_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layoutDirection="ltr">
|
||||
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView
|
||||
android:id="@+id/revanced_external_download_button"
|
||||
style="@style/YouTubePlayerButton"
|
||||
android:layout_width="48.0dip"
|
||||
android:layout_height="60.0dip"
|
||||
android:paddingTop="6.5dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:longClickable="false"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/revanced_yt_download_button"
|
||||
yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container"
|
||||
yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/slow_motion_video/materialsymbolsoutlined/slow_motion_video_wght200gradN25_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:yt="http://schemas.android.com/apk/res-auto" android:id="@+id/youtube_controls_bottom_ui_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:layoutDirection="ltr">
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView android:id="@+id/revanced_playback_speed_dialog_button" android:paddingLeft="12dp" android:paddingTop="22dp" android:paddingRight="12dp" android:paddingBottom="16dp" android:longClickable="false" android:layout_width="60dp" android:layout_height="60dp" android:src="@drawable/revanced_playback_speed_dialog_button" android:scaleType="center" yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container" yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" style="@style/YouTubePlayerButton"/>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:yt="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/youtube_controls_bottom_ui_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layoutDirection="ltr">
|
||||
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView
|
||||
android:id="@+id/revanced_playback_speed_dialog_button"
|
||||
style="@style/YouTubePlayerButton"
|
||||
android:layout_width="48.0dip"
|
||||
android:layout_height="60.0dip"
|
||||
android:paddingTop="6.0dp"
|
||||
android:paddingBottom="0dp"
|
||||
android:longClickable="false"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/revanced_playback_speed_dialog_button"
|
||||
yt:layout_constraintBottom_toTopOf="@+id/quick_actions_container"
|
||||
yt:layout_constraintRight_toLeftOf="@+id/fullscreen_button" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/adjust/materialsymbolsoutlined/adjust_wght200gradN25_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/fast_forward/materialsymbolsoutlined/fast_forward_wght200gradN25_24px.xml
|
||||
The icon has been mirrored and resized
|
||||
Changes made: The icon has been mirrored and resized
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/compare/materialsymbolsoutlined/compare_wght200gradN25_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/edit/materialsymbolsoutlined/edit_wght200gradN25_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/fast_forward/materialsymbolsoutlined/fast_forward_wght200gradN25_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/ajayyy/SponsorBlock/blob/e1d656f43f8b3cfb40e1c521e4103d61db756872/public/icons/PlayerStartIconSponsorBlocker.svg
|
||||
The SponsorBlock logo was inverted
|
||||
Changes made: The SponsorBlock logo was inverted.
|
||||
|
||||
|
||||
Copyright 2021 Ajay Ramachandran <dev@ajay.app>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/publish/materialsymbolsoutlined/publish_wght200gradN25_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/thumbs_up_down/materialsymbolsoutlined/thumbs_up_down_wght300gradN25_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView
|
||||
android:id="@+id/revanced_sb_create_segment_button"
|
||||
style="@style/YouTubePlayerButton"
|
||||
android:layout_width="@dimen/controls_overlay_action_button_size"
|
||||
android:layout_height="@dimen/controls_overlay_action_button_size"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_toStartOf="@+id/player_additional_view_container"
|
||||
android:padding="@dimen/controls_overlay_action_button_padding"
|
||||
android:src="@drawable/revanced_sb_logo" />
|
||||
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView
|
||||
android:id="@+id/revanced_sb_voting_button"
|
||||
style="@style/YouTubePlayerButton"
|
||||
@@ -25,4 +12,17 @@
|
||||
android:layout_toStartOf="@+id/revanced_sb_create_segment_button"
|
||||
android:padding="@dimen/controls_overlay_action_button_padding"
|
||||
android:src="@drawable/revanced_sb_voting" />
|
||||
|
||||
<com.google.android.libraries.youtube.common.ui.TouchImageView
|
||||
android:id="@+id/revanced_sb_create_segment_button"
|
||||
style="@style/YouTubePlayerButton"
|
||||
android:layout_width="@dimen/controls_overlay_action_button_size"
|
||||
android:layout_height="@dimen/controls_overlay_action_button_size"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_toStartOf="@+id/player_additional_view_container"
|
||||
android:padding="@dimen/controls_overlay_action_button_padding"
|
||||
android:src="@drawable/revanced_sb_logo" />
|
||||
</RelativeLayout>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/brightness_auto/materialsymbolsoutlined/brightness_auto_wght300_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/brightness_6/materialsymbolsoutlined/brightness_6_wght300_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/volume_off/materialsymbolsoutlined/volume_off_wght300_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
https://github.com/google/material-design-icons/blob/9beae745bb758f3ad56654fb377ea5cf62be4915/symbols/android/volume_up/materialsymbolsoutlined/volume_up_wght300_24px.xml
|
||||
The icon has been resized
|
||||
Changes made: Icon has been resized.
|
||||
|
||||
|
||||
Copyright 2022 Google
|
||||
|
||||
Reference in New Issue
Block a user