mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-24 11:11:03 +00:00
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:
@@ -0,0 +1,227 @@
|
||||
package app.revanced.extension.strava;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import com.strava.modularframework.data.Destination;
|
||||
import com.strava.modularframework.data.GenericLayoutModule;
|
||||
import com.strava.modularframework.data.GenericModuleField;
|
||||
import com.strava.modularframework.data.ListField;
|
||||
import com.strava.modularframework.data.ListProperties;
|
||||
import com.strava.modularframework.data.ModularComponent;
|
||||
import com.strava.modularframework.data.ModularEntry;
|
||||
import com.strava.modularframework.data.ModularEntryContainer;
|
||||
import com.strava.modularframework.data.ModularMenuItem;
|
||||
import com.strava.modularframework.data.Module;
|
||||
import com.strava.modularframework.data.MultiStateFieldDescriptor;
|
||||
import com.strava.modularframeworknetwork.ModularEntryNetworkContainer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public class HideDistractionsPatch {
|
||||
public static boolean upselling;
|
||||
public static boolean promo;
|
||||
public static boolean followSuggestions;
|
||||
public static boolean challengeSuggestions;
|
||||
public static boolean joinChallenge;
|
||||
public static boolean joinClub;
|
||||
public static boolean activityLookback;
|
||||
|
||||
public static List<ModularEntry> filterChildrenEntries(ModularEntry modularEntry) {
|
||||
if (hideModularEntry(modularEntry)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return modularEntry.getChildrenEntries$original().stream()
|
||||
.filter(childrenEntry -> !hideModularEntry(childrenEntry))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<ModularEntry> filterEntries(ModularEntryContainer modularEntryContainer) {
|
||||
if (hideModularEntryContainer(modularEntryContainer)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return modularEntryContainer.getEntries$original().stream()
|
||||
.filter(entry -> !hideModularEntry(entry))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<ModularEntry> filterEntries(ModularEntryNetworkContainer modularEntryNetworkContainer) {
|
||||
if (hideModularEntryNetworkContainer(modularEntryNetworkContainer)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return modularEntryNetworkContainer.getEntries$original().stream()
|
||||
.filter(entry -> !hideModularEntry(entry))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<ModularMenuItem> filterMenuItems(ModularEntryContainer modularEntryContainer) {
|
||||
if (hideModularEntryContainer(modularEntryContainer)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return modularEntryContainer.getMenuItems$original().stream()
|
||||
.filter(menuItem -> !hideModularMenuItem(menuItem))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static ListProperties filterProperties(ModularEntryContainer modularEntryContainer) {
|
||||
if (hideModularEntryContainer(modularEntryContainer)) {
|
||||
return null;
|
||||
}
|
||||
return modularEntryContainer.getProperties$original();
|
||||
}
|
||||
|
||||
public static ListProperties filterProperties(ModularEntryNetworkContainer modularEntryNetworkContainer) {
|
||||
if (hideModularEntryNetworkContainer(modularEntryNetworkContainer)) {
|
||||
return null;
|
||||
}
|
||||
return modularEntryNetworkContainer.getProperties$original();
|
||||
}
|
||||
|
||||
public static ListField filterField(ListProperties listProperties, String key) {
|
||||
ListField listField = listProperties.getField$original(key);
|
||||
if (hideListField(listField)) {
|
||||
return null;
|
||||
}
|
||||
return listField;
|
||||
}
|
||||
|
||||
public static List<ListField> filterFields(ListField listField) {
|
||||
if (hideListField(listField)) {
|
||||
return null;
|
||||
}
|
||||
return listField.getFields$original().stream()
|
||||
.filter(field -> !hideListField(field))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static List<Module> filterModules(ModularEntry modularEntry) {
|
||||
if (hideModularEntry(modularEntry)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return modularEntry.getModules$original().stream()
|
||||
.filter(module -> !hideModule(module))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static GenericModuleField filterField(GenericLayoutModule genericLayoutModule, String key) {
|
||||
if (hideGenericLayoutModule(genericLayoutModule)) {
|
||||
return null;
|
||||
}
|
||||
GenericModuleField field = genericLayoutModule.getField$original(key);
|
||||
if (hideGenericModuleField(field)) {
|
||||
return null;
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public static GenericModuleField[] filterFields(GenericLayoutModule genericLayoutModule) {
|
||||
if (hideGenericLayoutModule(genericLayoutModule)) {
|
||||
return new GenericModuleField[0];
|
||||
}
|
||||
return Arrays.stream(genericLayoutModule.getFields$original())
|
||||
.filter(field -> !hideGenericModuleField(field))
|
||||
.toArray(GenericModuleField[]::new);
|
||||
}
|
||||
|
||||
public static GenericLayoutModule[] filterSubmodules(GenericLayoutModule genericLayoutModule) {
|
||||
if (hideGenericLayoutModule(genericLayoutModule)) {
|
||||
return new GenericLayoutModule[0];
|
||||
}
|
||||
return Arrays.stream(genericLayoutModule.getSubmodules$original())
|
||||
.filter(submodule -> !hideGenericLayoutModule(submodule))
|
||||
.toArray(GenericLayoutModule[]::new);
|
||||
}
|
||||
|
||||
public static List<Module> filterSubmodules(ModularComponent modularComponent) {
|
||||
if (hideByName(modularComponent.getPage()) || hideByName(modularComponent.getElement())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return modularComponent.getSubmodules$original().stream()
|
||||
.filter(submodule -> !hideModule(submodule))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static Map<String, GenericModuleField> filterStateMap(MultiStateFieldDescriptor multiStateFieldDescriptor) {
|
||||
return multiStateFieldDescriptor.getStateMap$original().entrySet().stream()
|
||||
.filter(entry -> !hideGenericModuleField(entry.getValue()))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
}
|
||||
|
||||
private static boolean hideModule(Module module) {
|
||||
return module == null ||
|
||||
hideByName(module.getPage()) ||
|
||||
hideByName(module.getElement());
|
||||
}
|
||||
|
||||
private static boolean hideModularEntry(ModularEntry modularEntry) {
|
||||
return modularEntry == null ||
|
||||
hideByName(modularEntry.getPage()) ||
|
||||
hideByName(modularEntry.getElement()) ||
|
||||
hideByDestination(modularEntry.getDestination());
|
||||
}
|
||||
|
||||
private static boolean hideGenericLayoutModule(GenericLayoutModule genericLayoutModule) {
|
||||
try {
|
||||
return genericLayoutModule == null ||
|
||||
hideByName(genericLayoutModule.getPage()) ||
|
||||
hideByName(genericLayoutModule.getElement()) ||
|
||||
hideByDestination(genericLayoutModule.getDestination());
|
||||
} catch (RuntimeException getParentEntryOrThrowException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hideListField(ListField listField) {
|
||||
return listField == null ||
|
||||
hideByName(listField.getElement()) ||
|
||||
hideByDestination(listField.getDestination());
|
||||
}
|
||||
|
||||
private static boolean hideGenericModuleField(GenericModuleField genericModuleField) {
|
||||
return genericModuleField == null ||
|
||||
hideByName(genericModuleField.getElement()) ||
|
||||
hideByDestination(genericModuleField.getDestination());
|
||||
}
|
||||
|
||||
private static boolean hideModularEntryContainer(ModularEntryContainer modularEntryContainer) {
|
||||
return modularEntryContainer == null ||
|
||||
hideByName(modularEntryContainer.getPage());
|
||||
}
|
||||
|
||||
private static boolean hideModularEntryNetworkContainer(ModularEntryNetworkContainer modularEntryNetworkContainer) {
|
||||
return modularEntryNetworkContainer == null ||
|
||||
hideByName(modularEntryNetworkContainer.getPage());
|
||||
}
|
||||
|
||||
private static boolean hideModularMenuItem(ModularMenuItem modularMenuItem) {
|
||||
return modularMenuItem == null ||
|
||||
hideByName(modularMenuItem.getElementName()) ||
|
||||
hideByDestination(modularMenuItem.getDestination());
|
||||
}
|
||||
|
||||
private static boolean hideByName(String name) {
|
||||
return name != null && (
|
||||
upselling && name.contains("_upsell") ||
|
||||
promo && (name.equals("promo") || name.equals("top_of_tab_promo")) ||
|
||||
followSuggestions && name.equals("suggested_follows") ||
|
||||
challengeSuggestions && name.equals("suggested_challenges") ||
|
||||
joinChallenge && name.equals("challenge") ||
|
||||
joinClub && name.equals("club") ||
|
||||
activityLookback && name.equals("highlighted_activity_lookback")
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean hideByDestination(Destination destination) {
|
||||
if (destination == null) {
|
||||
return false;
|
||||
}
|
||||
String url = destination.getUrl();
|
||||
return url != null && (
|
||||
upselling && url.startsWith("strava://subscription/checkout")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public abstract class Destination implements Serializable {
|
||||
public abstract String getUrl();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public abstract class GenericLayoutModule implements Serializable, Module {
|
||||
public abstract Destination getDestination();
|
||||
|
||||
@Override
|
||||
public abstract String getElement();
|
||||
|
||||
public abstract GenericModuleField getField(String key);
|
||||
|
||||
// Added by patch.
|
||||
public abstract GenericModuleField getField$original(String key);
|
||||
|
||||
public abstract GenericModuleField[] getFields();
|
||||
|
||||
// Added by patch.
|
||||
public abstract GenericModuleField[] getFields$original();
|
||||
|
||||
@Override
|
||||
public abstract String getPage();
|
||||
|
||||
public abstract GenericLayoutModule[] getSubmodules();
|
||||
|
||||
// Added by patch.
|
||||
public abstract GenericLayoutModule[] getSubmodules$original();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public abstract class GenericModuleField implements Serializable {
|
||||
public abstract Destination getDestination();
|
||||
|
||||
public abstract String getElement();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ListField {
|
||||
public abstract Destination getDestination();
|
||||
|
||||
public abstract String getElement();
|
||||
|
||||
public abstract List<ListField> getFields();
|
||||
|
||||
// Added by patch.
|
||||
public abstract List<ListField> getFields$original();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
public abstract class ListProperties {
|
||||
public abstract ListField getField(String key);
|
||||
|
||||
// Added by patch.
|
||||
public abstract ListField getField$original(String key);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ModularComponent implements Module {
|
||||
@Override
|
||||
public abstract String getElement();
|
||||
|
||||
@Override
|
||||
public abstract String getPage();
|
||||
|
||||
public abstract List<Module> getSubmodules();
|
||||
|
||||
// Added by patch.
|
||||
public abstract List<Module> getSubmodules$original();
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ModularEntry {
|
||||
List<ModularEntry> getChildrenEntries();
|
||||
|
||||
// Added by patch.
|
||||
List<ModularEntry> getChildrenEntries$original();
|
||||
|
||||
Destination getDestination();
|
||||
|
||||
String getElement();
|
||||
|
||||
List<Module> getModules();
|
||||
|
||||
// Added by patch.
|
||||
List<Module> getModules$original();
|
||||
|
||||
String getPage();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ModularEntryContainer {
|
||||
public abstract List<ModularEntry> getEntries();
|
||||
|
||||
// Added by patch.
|
||||
public abstract List<ModularEntry> getEntries$original();
|
||||
|
||||
public abstract List<ModularMenuItem> getMenuItems();
|
||||
|
||||
// Added by patch.
|
||||
public abstract List<ModularMenuItem> getMenuItems$original();
|
||||
|
||||
public abstract String getPage();
|
||||
|
||||
public abstract ListProperties getProperties();
|
||||
|
||||
// Added by patch.
|
||||
public abstract ListProperties getProperties$original();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
public abstract class ModularMenuItem {
|
||||
public abstract Destination getDestination();
|
||||
|
||||
public abstract String getElementName();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
public interface Module {
|
||||
String getElement();
|
||||
|
||||
String getPage();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.strava.modularframework.data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class MultiStateFieldDescriptor {
|
||||
public abstract Map<String, GenericModuleField> getStateMap();
|
||||
|
||||
// Added by patch.
|
||||
public abstract Map<String, GenericModuleField> getStateMap$original();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.strava.modularframeworknetwork;
|
||||
|
||||
import com.strava.modularframework.data.ListProperties;
|
||||
import com.strava.modularframework.data.ModularEntry;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ModularEntryNetworkContainer {
|
||||
public abstract List<ModularEntry> getEntries();
|
||||
|
||||
// Added by patch.
|
||||
public abstract List<ModularEntry> getEntries$original();
|
||||
|
||||
public abstract String getPage();
|
||||
|
||||
public abstract ListProperties getProperties();
|
||||
|
||||
// Added by patch.
|
||||
public abstract ListProperties getProperties$original();
|
||||
}
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user