diff --git a/extensions/strava/src/main/java/app/revanced/extension/strava/HideDistractionsPatch.java b/extensions/strava/src/main/java/app/revanced/extension/strava/HideDistractionsPatch.java new file mode 100644 index 000000000..7d04a2ac6 --- /dev/null +++ b/extensions/strava/src/main/java/app/revanced/extension/strava/HideDistractionsPatch.java @@ -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 filterChildrenEntries(ModularEntry modularEntry) { + if (hideModularEntry(modularEntry)) { + return Collections.emptyList(); + } + return modularEntry.getChildrenEntries$original().stream() + .filter(childrenEntry -> !hideModularEntry(childrenEntry)) + .collect(Collectors.toList()); + } + + public static List filterEntries(ModularEntryContainer modularEntryContainer) { + if (hideModularEntryContainer(modularEntryContainer)) { + return Collections.emptyList(); + } + return modularEntryContainer.getEntries$original().stream() + .filter(entry -> !hideModularEntry(entry)) + .collect(Collectors.toList()); + } + + public static List filterEntries(ModularEntryNetworkContainer modularEntryNetworkContainer) { + if (hideModularEntryNetworkContainer(modularEntryNetworkContainer)) { + return Collections.emptyList(); + } + return modularEntryNetworkContainer.getEntries$original().stream() + .filter(entry -> !hideModularEntry(entry)) + .collect(Collectors.toList()); + } + + public static List 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 filterFields(ListField listField) { + if (hideListField(listField)) { + return null; + } + return listField.getFields$original().stream() + .filter(field -> !hideListField(field)) + .collect(Collectors.toList()); + } + + public static List 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 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 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") + ); + } +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/Destination.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/Destination.java new file mode 100644 index 000000000..eb46d6e51 --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/Destination.java @@ -0,0 +1,7 @@ +package com.strava.modularframework.data; + +import java.io.Serializable; + +public abstract class Destination implements Serializable { + public abstract String getUrl(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/GenericLayoutModule.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/GenericLayoutModule.java new file mode 100644 index 000000000..1b9d5afdd --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/GenericLayoutModule.java @@ -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(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/GenericModuleField.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/GenericModuleField.java new file mode 100644 index 000000000..f37d00462 --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/GenericModuleField.java @@ -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(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ListField.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ListField.java new file mode 100644 index 000000000..f639fbd85 --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ListField.java @@ -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 getFields(); + + // Added by patch. + public abstract List getFields$original(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ListProperties.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ListProperties.java new file mode 100644 index 000000000..1af0959ed --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ListProperties.java @@ -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); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularComponent.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularComponent.java new file mode 100644 index 000000000..f17ad5566 --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularComponent.java @@ -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 getSubmodules(); + + // Added by patch. + public abstract List getSubmodules$original(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularEntry.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularEntry.java new file mode 100644 index 000000000..38c51f0c2 --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularEntry.java @@ -0,0 +1,21 @@ +package com.strava.modularframework.data; + +import java.util.List; + +public interface ModularEntry { + List getChildrenEntries(); + + // Added by patch. + List getChildrenEntries$original(); + + Destination getDestination(); + + String getElement(); + + List getModules(); + + // Added by patch. + List getModules$original(); + + String getPage(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularEntryContainer.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularEntryContainer.java new file mode 100644 index 000000000..a7c172645 --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularEntryContainer.java @@ -0,0 +1,22 @@ +package com.strava.modularframework.data; + +import java.util.List; + +public abstract class ModularEntryContainer { + public abstract List getEntries(); + + // Added by patch. + public abstract List getEntries$original(); + + public abstract List getMenuItems(); + + // Added by patch. + public abstract List getMenuItems$original(); + + public abstract String getPage(); + + public abstract ListProperties getProperties(); + + // Added by patch. + public abstract ListProperties getProperties$original(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularMenuItem.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularMenuItem.java new file mode 100644 index 000000000..67c62b99b --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/ModularMenuItem.java @@ -0,0 +1,7 @@ +package com.strava.modularframework.data; + +public abstract class ModularMenuItem { + public abstract Destination getDestination(); + + public abstract String getElementName(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/Module.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/Module.java new file mode 100644 index 000000000..7affc6867 --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/Module.java @@ -0,0 +1,7 @@ +package com.strava.modularframework.data; + +public interface Module { + String getElement(); + + String getPage(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframework/data/MultiStateFieldDescriptor.java b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/MultiStateFieldDescriptor.java new file mode 100644 index 000000000..e1501d8e1 --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframework/data/MultiStateFieldDescriptor.java @@ -0,0 +1,10 @@ +package com.strava.modularframework.data; + +import java.util.Map; + +public abstract class MultiStateFieldDescriptor { + public abstract Map getStateMap(); + + // Added by patch. + public abstract Map getStateMap$original(); +} diff --git a/extensions/strava/stub/src/main/java/com/strava/modularframeworknetwork/ModularEntryNetworkContainer.java b/extensions/strava/stub/src/main/java/com/strava/modularframeworknetwork/ModularEntryNetworkContainer.java new file mode 100644 index 000000000..4ae689130 --- /dev/null +++ b/extensions/strava/stub/src/main/java/com/strava/modularframeworknetwork/ModularEntryNetworkContainer.java @@ -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 getEntries(); + + // Added by patch. + public abstract List getEntries$original(); + + public abstract String getPage(); + + public abstract ListProperties getProperties(); + + // Added by patch. + public abstract ListProperties getProperties$original(); +} diff --git a/patches/api/patches.api b/patches/api/patches.api index a593bbf97..892fe1d21 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -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; } diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/distractions/HideDistractionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/distractions/HideDistractionsPatch.kt new file mode 100644 index 000000000..a2d4eb54b --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/strava/distractions/HideDistractionsPatch.kt @@ -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 = 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, + ) { + 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 + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt index c91bb961a..93ef5337a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/strava/upselling/DisableSubscriptionSuggestionsPatch.kt @@ -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 + }) }