feat(Tumblr): Add Disable Tumblr Live
patch (#2987)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
parent
fbaf3cce03
commit
bf1f9dc799
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.tumblr.annoyances.notifications.patch
|
||||
package app.revanced.patches.tumblr.annoyances.notifications
|
||||
|
||||
import app.revanced.extensions.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
|
@ -1,4 +1,4 @@
|
|||
package app.revanced.patches.tumblr.annoyances.popups.patch
|
||||
package app.revanced.patches.tumblr.annoyances.popups
|
||||
|
||||
import app.revanced.extensions.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
|
@ -0,0 +1,116 @@
|
|||
package app.revanced.patches.tumblr.featureflags
|
||||
|
||||
import app.revanced.extensions.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.or
|
||||
import app.revanced.patcher.patch.BytecodePatch
|
||||
import app.revanced.patcher.patch.annotation.Patch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.tumblr.featureflags.fingerprints.GetFeatureValueFingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
|
||||
@Patch(description = "Forcibly set the value of A/B testing features of your choice.")
|
||||
object OverrideFeatureFlagsPatch : BytecodePatch(
|
||||
setOf(GetFeatureValueFingerprint)
|
||||
) {
|
||||
/**
|
||||
* Override a feature flag with a value.
|
||||
*
|
||||
* @param name The name of the feature flag to override.
|
||||
* @param value The value to override the feature flag with.
|
||||
*/
|
||||
@Suppress("KDocUnresolvedReference")
|
||||
internal lateinit var addOverride: (name: String, value: String) -> Unit private set
|
||||
|
||||
override fun execute(context: BytecodeContext) = GetFeatureValueFingerprint.result?.let {
|
||||
// The method we want to inject into does not have enough registers, so we inject a helper method
|
||||
// and inject more instructions into it later, see addOverride.
|
||||
// This is not in an integration since the unused variable would get compiled away and the method would
|
||||
// get compiled to only have one register, which is not enough for our later injected instructions.
|
||||
val helperMethod = ImmutableMethod(
|
||||
it.method.definingClass,
|
||||
"getValueOverride",
|
||||
listOf(ImmutableMethodParameter("Lcom/tumblr/configuration/Feature;", null, "feature")),
|
||||
"Ljava/lang/String;",
|
||||
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(4)
|
||||
).toMutable().apply {
|
||||
// This is the equivalent of
|
||||
// String featureName = feature.toString()
|
||||
// <inject more instructions here later>
|
||||
// return null
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
# toString() the enum value
|
||||
invoke-virtual {p1}, Lcom/tumblr/configuration/Feature;->toString()Ljava/lang/String;
|
||||
move-result-object v0
|
||||
|
||||
# !!! If you add more instructions above this line, update helperInsertIndex below!
|
||||
# Here we will insert one compare & return for every registered Feature override
|
||||
# This is done below in the addOverride function
|
||||
|
||||
# If none of the overrides returned a value, we should return null
|
||||
const/4 v0, 0x0
|
||||
return-object v0
|
||||
"""
|
||||
)
|
||||
}.also { helperMethod ->
|
||||
it.mutableClass.methods.add(helperMethod)
|
||||
}
|
||||
|
||||
// Here we actually insert the hook to call our helper method and return its value if it returns not null
|
||||
// This is equivalent to
|
||||
// String forcedValue = getValueOverride(feature)
|
||||
// if (forcedValue != null) return forcedValue
|
||||
val getFeatureIndex = it.scanResult.patternScanResult!!.startIndex
|
||||
it.mutableMethod.addInstructionsWithLabels(
|
||||
getFeatureIndex,
|
||||
"""
|
||||
# Call the Helper Method with the Feature
|
||||
invoke-virtual {p0, p1}, Lcom/tumblr/configuration/Configuration;->getValueOverride(Lcom/tumblr/configuration/Feature;)Ljava/lang/String;
|
||||
move-result-object v0
|
||||
# If it returned null, skip
|
||||
if-eqz v0, :is_null
|
||||
# If it didnt return null, return that string
|
||||
return-object v0
|
||||
|
||||
# If our override helper returned null, we let the function continue normally
|
||||
:is_null
|
||||
nop
|
||||
"""
|
||||
)
|
||||
|
||||
val helperInsertIndex = 2
|
||||
addOverride = { name, value ->
|
||||
// For every added override, we add a few instructions in the middle of the helper method
|
||||
// to check if the feature is the one we want and return the override value if it is.
|
||||
// This is equivalent to
|
||||
// if (featureName == {name}) return {value}
|
||||
helperMethod.addInstructionsWithLabels(
|
||||
helperInsertIndex,
|
||||
"""
|
||||
# v0 is still the string name of the currently checked feature from above
|
||||
# Compare the current string with the override string
|
||||
const-string v1, "$name"
|
||||
invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
|
||||
move-result v1
|
||||
# If the current string is the one we want to override, we return the override value
|
||||
if-eqz v1, :no_override
|
||||
const-string v1, "$value"
|
||||
return-object v1
|
||||
# Else we just continue...
|
||||
:no_override
|
||||
nop
|
||||
"""
|
||||
)
|
||||
}
|
||||
} ?: throw GetFeatureValueFingerprint.exception
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package app.revanced.patches.tumblr.featureflags.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
// This fingerprint targets the method to get the value of a Feature in the class "com.tumblr.configuration.Feature".
|
||||
// Features seem to be Tumblr's A/B testing program.
|
||||
// Feature states are loaded from the server in the "api-http2.tumblr.com/v2/config" request on (first) startup.
|
||||
// A lot of features are returned there, but most of them do not seem to do anything (anymore).
|
||||
// They were likely removed in newer App versions to always be on, but are still returned
|
||||
// as enabled for old App versions.
|
||||
// Some features seem to be very old and never removed, though, such as Google Login.
|
||||
// The startIndex of the opcode pattern is at the start of the function after the arg null check.
|
||||
// we want to insert our instructions there.
|
||||
object GetFeatureValueFingerprint : MethodFingerprint(
|
||||
strings = listOf("feature"),
|
||||
opcodes = listOf(
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.INVOKE_STATIC,
|
||||
Opcode.MOVE_RESULT
|
||||
),
|
||||
customFingerprint = { method, _ -> method.definingClass == "Lcom/tumblr/configuration/Configuration;" }
|
||||
)
|
|
@ -0,0 +1,37 @@
|
|||
package app.revanced.patches.tumblr.live
|
||||
|
||||
import app.revanced.extensions.exception
|
||||
import app.revanced.patcher.data.BytecodeContext
|
||||
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.patches.tumblr.featureflags.OverrideFeatureFlagsPatch
|
||||
import app.revanced.patches.tumblr.live.fingerprints.LiveMarqueeFingerprint
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
@Patch(
|
||||
name = "Disable Tumblr Live",
|
||||
description = "Disable the Tumblr Live tab button and dashboard carousel.",
|
||||
dependencies = [OverrideFeatureFlagsPatch::class],
|
||||
compatiblePackages = [CompatiblePackage("com.tumblr")]
|
||||
)
|
||||
@Suppress("unused")
|
||||
object DisableTumblrLivePatch : BytecodePatch(
|
||||
setOf(LiveMarqueeFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext) = LiveMarqueeFingerprint.result?.let {
|
||||
it.scanResult.stringsScanResult!!.matches.forEach { match ->
|
||||
// Replace the string constant "live_marquee"
|
||||
// with a dummy so the app doesn't recognize this type of element in the Dashboard and skips it
|
||||
it.mutableMethod.apply {
|
||||
val stringRegister = getInstruction<OneRegisterInstruction>(match.index).registerA
|
||||
replaceInstruction(match.index, "const-string v$stringRegister, \"dummy2\"")
|
||||
}
|
||||
}
|
||||
|
||||
// We hide the Tab button for Tumblr Live by forcing the feature flag to false
|
||||
OverrideFeatureFlagsPatch.addOverride("liveStreaming", "false")
|
||||
} ?: throw LiveMarqueeFingerprint.exception
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package app.revanced.patches.tumblr.live.fingerprints
|
||||
|
||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||
|
||||
// This works identically to the Tumblr AdWaterfallFingerprint, see comments there
|
||||
object LiveMarqueeFingerprint : MethodFingerprint(strings = listOf("live_marquee"))
|
Loading…
Reference in a new issue