feat(Strava): Add Hide distractions patch (#6479)
Co-authored-by: Pun Butrach <pun.butrach@gmail.com> Co-authored-by: ekaunt <62402760+ekaunt@users.noreply.github.com> Co-authored-by: bengross <bengross@vecta.com> Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
parent
421cb2899e
commit
66b0852f8f
16 changed files with 601 additions and 56 deletions
|
|
@ -1236,6 +1236,10 @@ public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatchKt {
|
|||
public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/strava/distractions/HideDistractionsPatchKt {
|
||||
public static final fun getHideDistractionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivityKt {
|
||||
public static final fun getAddGiveGroupKudosButtonToGroupActivity ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,191 @@
|
|||
package app.revanced.patches.strava.distractions
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.patch.booleanOption
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableBooleanEncodedValue.Companion.toMutable
|
||||
import app.revanced.patches.strava.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.findMutableMethodOf
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.value.ImmutableBooleanEncodedValue
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import java.util.logging.Logger
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/strava/HideDistractionsPatch;"
|
||||
private const val MODULAR_FRAMEWORK_CLASS_DESCRIPTOR_PREFIX = "Lcom/strava/modularframework"
|
||||
|
||||
private const val METHOD_SUFFIX = "\$original"
|
||||
|
||||
private data class FilterablePropertyFingerprint(
|
||||
val name: String,
|
||||
val parameterTypes: List<String> = listOf(),
|
||||
)
|
||||
|
||||
private val fingerprints = arrayOf(
|
||||
FilterablePropertyFingerprint("ChildrenEntries"),
|
||||
FilterablePropertyFingerprint("Entries"),
|
||||
FilterablePropertyFingerprint("Field", listOf("Ljava/lang/String;")),
|
||||
FilterablePropertyFingerprint("Fields"),
|
||||
FilterablePropertyFingerprint("MenuItems"),
|
||||
FilterablePropertyFingerprint("Modules"),
|
||||
FilterablePropertyFingerprint("Properties"),
|
||||
FilterablePropertyFingerprint("StateMap"),
|
||||
FilterablePropertyFingerprint("Submodules"),
|
||||
)
|
||||
|
||||
@Suppress("unused")
|
||||
val hideDistractionsPatch = bytecodePatch(
|
||||
name = "Hide distractions",
|
||||
description = "Hides elements that are not essential.",
|
||||
) {
|
||||
compatibleWith("com.strava")
|
||||
|
||||
dependsOn(sharedExtensionPatch)
|
||||
|
||||
val logger = Logger.getLogger(this::class.java.name)
|
||||
|
||||
val options = arrayOf(
|
||||
booleanOption(
|
||||
key = "upselling",
|
||||
title = "Upselling",
|
||||
description = "Elements that suggest you subscribe.",
|
||||
default = true,
|
||||
required = true,
|
||||
),
|
||||
booleanOption(
|
||||
key = "promo",
|
||||
title = "Promotions",
|
||||
default = true,
|
||||
required = true,
|
||||
),
|
||||
booleanOption(
|
||||
key = "followSuggestions",
|
||||
title = "Who to Follow",
|
||||
description = "Popular athletes, followers, people near you etc.",
|
||||
default = true,
|
||||
required = true,
|
||||
),
|
||||
booleanOption(
|
||||
key = "challengeSuggestions",
|
||||
title = "Suggested Challenges",
|
||||
description = "Random challenges Strava wants you to join.",
|
||||
default = true,
|
||||
required = true,
|
||||
),
|
||||
booleanOption(
|
||||
key = "joinChallenge",
|
||||
title = "Join Challenge",
|
||||
description = "Challenges your follows have joined.",
|
||||
default = false,
|
||||
required = true,
|
||||
),
|
||||
booleanOption(
|
||||
key = "joinClub",
|
||||
title = "Joined a club",
|
||||
description = "Clubs your follows have joined.",
|
||||
default = false,
|
||||
required = true,
|
||||
),
|
||||
booleanOption(
|
||||
key = "activityLookback",
|
||||
title = "Your activity from X years ago",
|
||||
default = false,
|
||||
required = true,
|
||||
),
|
||||
)
|
||||
|
||||
execute {
|
||||
// region Write option values into extension class.
|
||||
|
||||
val extensionClass = classBy { it.type == EXTENSION_CLASS_DESCRIPTOR }!!.mutableClass.apply {
|
||||
options.forEach { option ->
|
||||
staticFields.first { field -> field.name == option.key }.initialValue =
|
||||
ImmutableBooleanEncodedValue.forBoolean(option.value == true).toMutable()
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Intercept all classes' property getter calls.
|
||||
|
||||
fun MutableMethod.cloneAndIntercept(
|
||||
classDef: MutableClass,
|
||||
extensionMethodName: String,
|
||||
extensionMethodParameterTypes: List<String>,
|
||||
) {
|
||||
val extensionMethodReference = ImmutableMethodReference(
|
||||
EXTENSION_CLASS_DESCRIPTOR,
|
||||
extensionMethodName,
|
||||
extensionMethodParameterTypes,
|
||||
returnType,
|
||||
)
|
||||
|
||||
if (extensionClass.directMethods.none { method ->
|
||||
MethodUtil.methodSignaturesMatch(method, extensionMethodReference)
|
||||
}) {
|
||||
logger.info { "Skipped interception of $this due to missing $extensionMethodReference" }
|
||||
return
|
||||
}
|
||||
|
||||
classDef.virtualMethods -= this
|
||||
|
||||
val clone = ImmutableMethod.of(this).toMutable()
|
||||
|
||||
classDef.virtualMethods += clone
|
||||
|
||||
if (implementation != null) {
|
||||
val registers = List(extensionMethodParameterTypes.size) { index -> "p$index" }.joinToString(
|
||||
separator = ",",
|
||||
prefix = "{",
|
||||
postfix = "}",
|
||||
)
|
||||
|
||||
clone.addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static $registers, $extensionMethodReference
|
||||
move-result-object v0
|
||||
return-object v0
|
||||
"""
|
||||
)
|
||||
|
||||
logger.fine { "Intercepted $this with $extensionMethodReference" }
|
||||
}
|
||||
|
||||
name += METHOD_SUFFIX
|
||||
|
||||
classDef.virtualMethods += this
|
||||
}
|
||||
|
||||
classes.filter { it.type.startsWith(MODULAR_FRAMEWORK_CLASS_DESCRIPTOR_PREFIX) }.forEach { classDef ->
|
||||
val classDefProxy by lazy { proxy(classDef) }
|
||||
|
||||
classDef.virtualMethods.forEach { method ->
|
||||
fingerprints.find { fingerprint ->
|
||||
method.name == "get${fingerprint.name}" && method.parameterTypes == fingerprint.parameterTypes
|
||||
}?.let { fingerprint ->
|
||||
classDefProxy.mutableClass.let { mutableClass ->
|
||||
// Upcast to the interface if this is an interface implementation.
|
||||
val parameterType = classDef.interfaces.find {
|
||||
classes.find { interfaceDef -> interfaceDef.type == it }?.virtualMethods?.any { interfaceMethod ->
|
||||
MethodUtil.methodSignaturesMatch(interfaceMethod, method)
|
||||
} == true
|
||||
} ?: classDef.type
|
||||
|
||||
mutableClass.findMutableMethodOf(method).cloneAndIntercept(
|
||||
mutableClass,
|
||||
"filter${fingerprint.name}",
|
||||
listOf(parameterType) + fingerprint.parameterTypes
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +1,22 @@
|
|||
package app.revanced.patches.strava.upselling
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import app.revanced.patches.strava.distractions.hideDistractionsPatch
|
||||
|
||||
@Suppress("unused")
|
||||
@Deprecated("Superseded by \"Hide distractions\" patch", ReplaceWith("hideDistractionsPatch"))
|
||||
val disableSubscriptionSuggestionsPatch = bytecodePatch(
|
||||
name = "Disable subscription suggestions",
|
||||
) {
|
||||
compatibleWith("com.strava")
|
||||
|
||||
execute {
|
||||
val helperMethodName = "getModulesIfNotUpselling"
|
||||
val pageSuffix = "_upsell"
|
||||
val label = "original"
|
||||
|
||||
val className = getModulesFingerprint.originalClassDef.type
|
||||
val originalMethod = getModulesFingerprint.method
|
||||
val returnType = originalMethod.returnType
|
||||
|
||||
getModulesFingerprint.classDef.methods.add(
|
||||
ImmutableMethod(
|
||||
className,
|
||||
helperMethodName,
|
||||
emptyList(),
|
||||
returnType,
|
||||
AccessFlags.PRIVATE.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(3),
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
"""
|
||||
iget-object v0, p0, $className->page:Ljava/lang/String;
|
||||
const-string v1, "$pageSuffix"
|
||||
invoke-virtual {v0, v1}, Ljava/lang/String;->endsWith(Ljava/lang/String;)Z
|
||||
move-result v0
|
||||
if-eqz v0, :$label
|
||||
invoke-static {}, Ljava/util/Collections;->emptyList()Ljava/util/List;
|
||||
move-result-object v0
|
||||
return-object v0
|
||||
:$label
|
||||
iget-object v0, p0, $className->modules:Ljava/util/List;
|
||||
return-object v0
|
||||
""",
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
val getModulesIndex = getModulesFingerprint.patternMatch!!.startIndex
|
||||
with(originalMethod) {
|
||||
removeInstruction(getModulesIndex)
|
||||
addInstructions(
|
||||
getModulesIndex,
|
||||
"""
|
||||
invoke-direct {p0}, $className->$helperMethodName()$returnType
|
||||
move-result-object v0
|
||||
""",
|
||||
)
|
||||
}
|
||||
}
|
||||
dependsOn(hideDistractionsPatch.apply {
|
||||
options["upselling"] = true
|
||||
options["promo"] = false
|
||||
options["followSuggestions"] = false
|
||||
options["challengeSuggestions"] = false
|
||||
options["joinChallenge"] = false
|
||||
options["joinClub"] = false
|
||||
options["activityLookback"] = false
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue