mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-25 11:41:04 +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 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 final class app/revanced/patches/strava/groupkudos/AddGiveGroupKudosButtonToGroupActivityKt {
|
||||||
public static final fun getAddGiveGroupKudosButtonToGroupActivity ()Lapp/revanced/patcher/patch/BytecodePatch;
|
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
|
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.patch.bytecodePatch
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
import app.revanced.patches.strava.distractions.hideDistractionsPatch
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
@Deprecated("Superseded by \"Hide distractions\" patch", ReplaceWith("hideDistractionsPatch"))
|
||||||
val disableSubscriptionSuggestionsPatch = bytecodePatch(
|
val disableSubscriptionSuggestionsPatch = bytecodePatch(
|
||||||
name = "Disable subscription suggestions",
|
name = "Disable subscription suggestions",
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.strava")
|
compatibleWith("com.strava")
|
||||||
|
|
||||||
execute {
|
dependsOn(hideDistractionsPatch.apply {
|
||||||
val helperMethodName = "getModulesIfNotUpselling"
|
options["upselling"] = true
|
||||||
val pageSuffix = "_upsell"
|
options["promo"] = false
|
||||||
val label = "original"
|
options["followSuggestions"] = false
|
||||||
|
options["challengeSuggestions"] = false
|
||||||
val className = getModulesFingerprint.originalClassDef.type
|
options["joinChallenge"] = false
|
||||||
val originalMethod = getModulesFingerprint.method
|
options["joinClub"] = false
|
||||||
val returnType = originalMethod.returnType
|
options["activityLookback"] = false
|
||||||
|
})
|
||||||
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
|
|
||||||
""",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user