mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-20 01:23:57 +00:00
Compare commits
9 Commits
v5.48.0-de
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef052c0d8f | ||
|
|
d9fa580222 | ||
|
|
182224c79d | ||
|
|
83c0127ebb | ||
|
|
3762f1de08 | ||
|
|
18c0b04f0c | ||
|
|
4c4ba1c78c | ||
|
|
7cef24a5e9 | ||
|
|
8725a49ba3 |
56
CHANGELOG.md
56
CHANGELOG.md
@@ -1,3 +1,59 @@
|
||||
# [5.48.0](https://github.com/ReVanced/revanced-patches/compare/v5.47.0...v5.48.0) (2026-01-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Boost for Reddit - Fix missing audio in video downloads:** Make it work again by reflecting Reddits latest changes ([#6500](https://github.com/ReVanced/revanced-patches/issues/6500)) ([eecc44b](https://github.com/ReVanced/revanced-patches/commit/eecc44b9567bf2ca72ac99e0dafa483a6803c0f9))
|
||||
* **Disney+ - Skip ads:** Remove unsupported package names ([#6422](https://github.com/ReVanced/revanced-patches/issues/6422)) ([44e7dbc](https://github.com/ReVanced/revanced-patches/commit/44e7dbcf4d7eaf94dd0164baba847d3e19250154))
|
||||
* Fix build error introduced in `4046bee` ([#6417](https://github.com/ReVanced/revanced-patches/issues/6417)) ([789f0a5](https://github.com/ReVanced/revanced-patches/commit/789f0a562861825065633d172445ebf35a1ba8d8))
|
||||
* Fix compilation error introduced in `6bb6281` ([#6409](https://github.com/ReVanced/revanced-patches/issues/6409)) ([71c6cb5](https://github.com/ReVanced/revanced-patches/commit/71c6cb569ebf7b93cf73ee391839e5220557ce7c))
|
||||
* Fix compilation error introduced in dc69f243 ([#6392](https://github.com/ReVanced/revanced-patches/issues/6392)) ([a429824](https://github.com/ReVanced/revanced-patches/commit/a429824bb77b49aea14b0b54f2204ae24d5209a1))
|
||||
* **Instagram:** `Sanitize sharing links` ([#6483](https://github.com/ReVanced/revanced-patches/issues/6483)) ([8724759](https://github.com/ReVanced/revanced-patches/commit/87247590de3db74680cb02ba1d87bf683b2269e2))
|
||||
* **YouTube - Hide layout components:** Hide new type of crowdfunding box ([#6380](https://github.com/ReVanced/revanced-patches/issues/6380)) ([dc69f24](https://github.com/ReVanced/revanced-patches/commit/dc69f2433e2650654e2dffdd76b0b0c8a52bf515))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Disable Sentry telemetry` patch ([#6416](https://github.com/ReVanced/revanced-patches/issues/6416)) ([4cc3159](https://github.com/ReVanced/revanced-patches/commit/4cc315952db557c565872de9e8484805f2e42305))
|
||||
* Add `Prevent screenshot detection` patch ([#6482](https://github.com/ReVanced/revanced-patches/issues/6482)) ([83c0127](https://github.com/ReVanced/revanced-patches/commit/83c0127ebb8f53ab8a067758619faaac5596c145))
|
||||
* Disable Play Integrity patch ([#6412](https://github.com/ReVanced/revanced-patches/issues/6412)) ([6312fe8](https://github.com/ReVanced/revanced-patches/commit/6312fe8d60da24465c0c1b0fa4e94ceb79873d9c))
|
||||
* **Instagram - Hides navigation buttons:** Add more buttons to hide ([#6390](https://github.com/ReVanced/revanced-patches/issues/6390)) ([6bb6281](https://github.com/ReVanced/revanced-patches/commit/6bb62811493da04812cc3e392e68d874f95cbef9))
|
||||
* **Instagram:** Add `Hide highlights tray` patch ([#6489](https://github.com/ReVanced/revanced-patches/issues/6489)) ([8725a49](https://github.com/ReVanced/revanced-patches/commit/8725a49ba3a06fee0280ffcf4be62cd960cd301e))
|
||||
* **Instagram:** Add `Remove build expired popup` patch ([#6488](https://github.com/ReVanced/revanced-patches/issues/6488)) ([18c0b04](https://github.com/ReVanced/revanced-patches/commit/18c0b04f0cd1bf8cd78b05af3b8ebe3a6a5f9e48))
|
||||
* **Instagram:** Disable `Disable Reels scrolling` by default ([3401467](https://github.com/ReVanced/revanced-patches/commit/3401467a6d49fc75b6757a15e5c848330c1b7307))
|
||||
* **Letterboxd:** Add `Unlock app icons` patch ([#6415](https://github.com/ReVanced/revanced-patches/issues/6415)) ([d25dcfe](https://github.com/ReVanced/revanced-patches/commit/d25dcfe49ac331c9b3dca739ba0be95dbab669cc))
|
||||
* **ProtonVPN:** Add `Unlock split tunneling` patch ([#6353](https://github.com/ReVanced/revanced-patches/issues/6353)) ([e0f3346](https://github.com/ReVanced/revanced-patches/commit/e0f33468e6e96b9f10cf35ec67622d6488528c90))
|
||||
* **SBS On Demand:** Add `Remove ads` patch ([#6378](https://github.com/ReVanced/revanced-patches/issues/6378)) ([315931c](https://github.com/ReVanced/revanced-patches/commit/315931cbf8f61cd4b3a54ace1ff03685d748614c))
|
||||
* **Strava:** Add `Add 'Give Kudos' button to 'Group Activity'` patch ([#6475](https://github.com/ReVanced/revanced-patches/issues/6475)) ([4c4ba1c](https://github.com/ReVanced/revanced-patches/commit/4c4ba1c78c9f4568a2b572f5c69e9c6c734e1a7f))
|
||||
* **Strava:** Add `Add media download` patch ([#6449](https://github.com/ReVanced/revanced-patches/issues/6449)) ([778d13c](https://github.com/ReVanced/revanced-patches/commit/778d13ce8b28ca6df3a665530320e4a21a27ae44))
|
||||
* **Strava:** Add `Block Snowplow tracking` patch ([#6413](https://github.com/ReVanced/revanced-patches/issues/6413)) ([c47beae](https://github.com/ReVanced/revanced-patches/commit/c47beae21376dd17ab8bc09afe73e9094481bde9))
|
||||
* **Strava:** Add `Disable Quick Edit` patch ([#6452](https://github.com/ReVanced/revanced-patches/issues/6452)) ([f5cbb31](https://github.com/ReVanced/revanced-patches/commit/f5cbb31724d15f7e939b96ee0186fd0a108f9fdc))
|
||||
* **Strava:** Add `Enable password login` patch ([#6396](https://github.com/ReVanced/revanced-patches/issues/6396)) ([8f3f4c9](https://github.com/ReVanced/revanced-patches/commit/8f3f4c95bb8f151fc9a2c272bf7d0e905c2f01fc))
|
||||
* **Strava:** Add `Overwrite media upload parameters` patch ([#6410](https://github.com/ReVanced/revanced-patches/issues/6410)) ([b42ae27](https://github.com/ReVanced/revanced-patches/commit/b42ae27ce66ebad9e9cfc5b70fc121df5bad7567))
|
||||
* **YouTube:** Add `Pause on audio interrupt` patch ([#6464](https://github.com/ReVanced/revanced-patches/issues/6464)) ([19f146c](https://github.com/ReVanced/revanced-patches/commit/19f146c01dc381b3cccd61e61ba4901872ff12d8))
|
||||
|
||||
# [5.48.0-dev.13](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.12...v5.48.0-dev.13) (2026-01-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add `Prevent screenshot detection` patch ([#6482](https://github.com/ReVanced/revanced-patches/issues/6482)) ([83c0127](https://github.com/ReVanced/revanced-patches/commit/83c0127ebb8f53ab8a067758619faaac5596c145))
|
||||
|
||||
# [5.48.0-dev.12](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.11...v5.48.0-dev.12) (2026-01-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Instagram:** Add `Remove build expired popup` patch ([#6488](https://github.com/ReVanced/revanced-patches/issues/6488)) ([18c0b04](https://github.com/ReVanced/revanced-patches/commit/18c0b04f0cd1bf8cd78b05af3b8ebe3a6a5f9e48))
|
||||
* **Strava:** Add `Add 'Give Kudos' button to 'Group Activity'` patch ([#6475](https://github.com/ReVanced/revanced-patches/issues/6475)) ([4c4ba1c](https://github.com/ReVanced/revanced-patches/commit/4c4ba1c78c9f4568a2b572f5c69e9c6c734e1a7f))
|
||||
|
||||
# [5.48.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.10...v5.48.0-dev.11) (2026-01-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **Instagram:** Add `Hide highlights tray` patch ([#6489](https://github.com/ReVanced/revanced-patches/issues/6489)) ([8725a49](https://github.com/ReVanced/revanced-patches/commit/8725a49ba3a06fee0280ffcf4be62cd960cd301e))
|
||||
|
||||
# [5.48.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.9...v5.48.0-dev.10) (2026-01-19)
|
||||
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.48.0-dev.10
|
||||
version = 5.48.0
|
||||
|
||||
@@ -124,6 +124,10 @@ public final class app/revanced/patches/all/misc/screencapture/RemoveScreenCaptu
|
||||
public static final fun getRemoveScreenCaptureRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/screenshot/PreventScreenshotDetectionPatchKt {
|
||||
public static final fun getPreventScreenshotDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/all/misc/screenshot/RemoveScreenshotRestrictionPatchKt {
|
||||
public static final fun getRemoveScreenshotRestrictionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -308,6 +312,10 @@ public final class app/revanced/patches/instagram/hide/explore/HideExploreFeedKt
|
||||
public static final fun getHideExploreFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/hide/highlightsTray/HideHighlightsTrayPatchKt {
|
||||
public static final fun getHideHighlightsTrayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/hide/navigation/HideNavigationButtonsKt {
|
||||
public static final fun getHideNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -332,6 +340,10 @@ public final class app/revanced/patches/instagram/misc/links/OpenLinksExternally
|
||||
public static final fun getOpenLinksExternallyPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/misc/removeBuildExpiredPopup/RemoveBuildExpiredPopupPatchKt {
|
||||
public static final fun getRemoveBuildExpiredPopupPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/instagram/misc/share/domain/ChangeLinkSharingDomainPatchKt {
|
||||
public static final fun getChangeLinkSharingDomainPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1200,6 +1212,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/groupkudos/AddGiveGroupKudosButtonToGroupActivityKt {
|
||||
public static final fun getAddGiveGroupKudosButtonToGroupActivity ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/strava/media/download/AddMediaDownloadPatchKt {
|
||||
public static final fun getAddMediaDownloadPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package app.revanced.patches.all.misc.screenshot
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
|
||||
private val registerScreenCaptureCallbackMethodReference = ImmutableMethodReference(
|
||||
"Landroid/app/Activity;",
|
||||
"registerScreenCaptureCallback",
|
||||
listOf(
|
||||
"Ljava/util/concurrent/Executor;",
|
||||
"Landroid/app/Activity\$ScreenCaptureCallback;",
|
||||
),
|
||||
"V"
|
||||
)
|
||||
|
||||
private val unregisterScreenCaptureCallbackMethodReference = ImmutableMethodReference(
|
||||
"Landroid/app/Activity;",
|
||||
"unregisterScreenCaptureCallback",
|
||||
listOf(
|
||||
"Landroid/app/Activity\$ScreenCaptureCallback;",
|
||||
),
|
||||
"V"
|
||||
)
|
||||
|
||||
@Suppress("unused")
|
||||
val preventScreenshotDetectionPatch = bytecodePatch(
|
||||
name = "Prevent screenshot detection",
|
||||
description = "Removes the registration of all screen capture callbacks. This prevents the app from detecting screenshots.",
|
||||
) {
|
||||
dependsOn(transformInstructionsPatch(
|
||||
filterMap = { _, _, instruction, instructionIndex ->
|
||||
if (instruction.opcode != Opcode.INVOKE_VIRTUAL) return@transformInstructionsPatch null
|
||||
|
||||
val reference = instruction.getReference<MethodReference>() ?: return@transformInstructionsPatch null
|
||||
|
||||
instructionIndex.takeIf {
|
||||
MethodUtil.methodSignaturesMatch(reference, registerScreenCaptureCallbackMethodReference) ||
|
||||
MethodUtil.methodSignaturesMatch(reference, unregisterScreenCaptureCallbackMethodReference)
|
||||
}
|
||||
},
|
||||
transform = { mutableMethod, instructionIndex ->
|
||||
mutableMethod.removeInstruction(instructionIndex)
|
||||
}
|
||||
))
|
||||
}
|
||||
@@ -1,29 +1,7 @@
|
||||
package app.revanced.patches.instagram.hide.explore
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
context(BytecodePatchContext)
|
||||
internal fun Fingerprint.replaceJsonFieldWithBogus(
|
||||
key: String,
|
||||
) {
|
||||
val targetStringIndex = stringMatches!!.first { match -> match.string == key }.index
|
||||
val targetStringRegister = method.getInstruction<OneRegisterInstruction>(targetStringIndex).registerA
|
||||
|
||||
/**
|
||||
* Replacing the JSON key we want to skip with a random string that is not a valid JSON key.
|
||||
* This way the feeds array will never be populated.
|
||||
* Received JSON keys that are not handled are simply ignored, so there are no side effects.
|
||||
*/
|
||||
method.replaceInstruction(
|
||||
targetStringIndex,
|
||||
"const-string v$targetStringRegister, \"BOGUS\"",
|
||||
)
|
||||
}
|
||||
import app.revanced.patches.instagram.shared.replaceStringWithBogus
|
||||
|
||||
@Suppress("unused")
|
||||
val hideExploreFeedPatch = bytecodePatch(
|
||||
@@ -34,6 +12,6 @@ val hideExploreFeedPatch = bytecodePatch(
|
||||
compatibleWith("com.instagram.android")
|
||||
|
||||
execute {
|
||||
exploreResponseJsonParserFingerprint.replaceJsonFieldWithBogus(EXPLORE_KEY_TO_BE_HIDDEN)
|
||||
exploreResponseJsonParserFingerprint.replaceStringWithBogus(EXPLORE_KEY_TO_BE_HIDDEN)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package app.revanced.patches.instagram.hide.highlightsTray
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
internal const val TARGET_STRING = "highlights_tray"
|
||||
|
||||
internal val highlightsUrlBuilderFingerprint = fingerprint {
|
||||
strings(TARGET_STRING,"X-IG-Accept-Hint")
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package app.revanced.patches.instagram.hide.highlightsTray
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.instagram.shared.replaceStringWithBogus
|
||||
|
||||
@Suppress("unused")
|
||||
val hideHighlightsTrayPatch = bytecodePatch(
|
||||
name = "Hide highlights tray",
|
||||
description = "Hides the highlights tray in profile section.",
|
||||
use = false
|
||||
) {
|
||||
compatibleWith("com.instagram.android")
|
||||
|
||||
execute {
|
||||
highlightsUrlBuilderFingerprint.replaceStringWithBogus(TARGET_STRING)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package app.revanced.patches.instagram.hide.suggestions
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.instagram.hide.explore.replaceJsonFieldWithBogus
|
||||
import app.revanced.patches.instagram.shared.replaceStringWithBogus
|
||||
|
||||
@Suppress("unused")
|
||||
val hideSuggestedContent = bytecodePatch(
|
||||
@@ -13,7 +13,7 @@ val hideSuggestedContent = bytecodePatch(
|
||||
|
||||
execute {
|
||||
FEED_ITEM_KEYS_TO_BE_HIDDEN.forEach { key ->
|
||||
feedItemParseFromJsonFingerprint.replaceJsonFieldWithBogus(key)
|
||||
feedItemParseFromJsonFingerprint.replaceStringWithBogus(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
package app.revanced.patches.instagram.misc.removeBuildExpiredPopup
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.literal
|
||||
|
||||
internal const val MILLISECOND_IN_A_DAY_LITERAL = 0x5265c00L
|
||||
|
||||
internal val appUpdateLockoutBuilderFingerprint = fingerprint {
|
||||
strings("android.hardware.sensor.hinge_angle")
|
||||
literal { MILLISECOND_IN_A_DAY_LITERAL }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package app.revanced.patches.instagram.misc.removeBuildExpiredPopup
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
|
||||
@Suppress("unused")
|
||||
val removeBuildExpiredPopupPatch = bytecodePatch(
|
||||
name = "Remove build expired popup",
|
||||
description = "Removes the popup that appears after a while, when the app version ages.",
|
||||
) {
|
||||
compatibleWith("com.instagram.android")
|
||||
|
||||
execute {
|
||||
appUpdateLockoutBuilderFingerprint.method.apply {
|
||||
val longToIntIndex = instructions.first { it.opcode == Opcode.LONG_TO_INT }.location.index
|
||||
val appAgeRegister = getInstruction<TwoRegisterInstruction>(longToIntIndex).registerA
|
||||
|
||||
// Set app age to 0 days old such that the build expired popup doesn't appear.
|
||||
addInstruction(longToIntIndex + 1, "const v$appAgeRegister, 0x0")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package app.revanced.patches.instagram.shared
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.BytecodePatchContext
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
|
||||
context(BytecodePatchContext)
|
||||
internal fun Fingerprint.replaceStringWithBogus(
|
||||
targetString: String,
|
||||
) {
|
||||
val targetStringIndex = stringMatches!!.first { match -> match.string == targetString }.index
|
||||
val targetStringRegister = method.getInstruction<OneRegisterInstruction>(targetStringIndex).registerA
|
||||
|
||||
/**
|
||||
* Replaces the 'target string' with 'BOGUS'.
|
||||
* This is usually done when we need to override a JSON key or url,
|
||||
* to skip with a random string that is not a valid JSON key.
|
||||
*/
|
||||
method.replaceInstruction(
|
||||
targetStringIndex,
|
||||
"const-string v$targetStringRegister, \"BOGUS\"",
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package app.revanced.patches.strava.groupkudos
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.util.childElementsSequence
|
||||
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.AccessFlags.*
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11x
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction31i
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableClassDef
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
import org.w3c.dom.Element
|
||||
|
||||
private const val VIEW_CLASS_DESCRIPTOR = "Landroid/view/View;"
|
||||
private const val ON_CLICK_LISTENER_CLASS_DESCRIPTOR = "Landroid/view/View\$OnClickListener;"
|
||||
|
||||
private var shakeToKudosStringId = -1
|
||||
private var kudosIdId = -1
|
||||
private var leaveIdId = -1
|
||||
|
||||
private val addGiveKudosButtonToLayoutPatch = resourcePatch {
|
||||
fun String.toResourceId() = substring(2).toInt(16)
|
||||
|
||||
execute {
|
||||
document("res/values/public.xml").use { public ->
|
||||
fun Sequence<Element>.firstByName(name: String) = first {
|
||||
it.getAttribute("name") == name
|
||||
}
|
||||
|
||||
val publicElements = public.documentElement.childElementsSequence().filter {
|
||||
it.tagName == "public"
|
||||
}
|
||||
val idElements = publicElements.filter {
|
||||
it.getAttribute("type") == "id"
|
||||
}
|
||||
val stringElements = publicElements.filter {
|
||||
it.getAttribute("type") == "string"
|
||||
}
|
||||
|
||||
shakeToKudosStringId =
|
||||
stringElements.firstByName("shake_to_kudos_dialog_title").getAttribute("id").toResourceId()
|
||||
|
||||
val kudosIdNode = idElements.firstByName("kudos").apply {
|
||||
kudosIdId = getAttribute("id").toResourceId()
|
||||
}
|
||||
|
||||
document("res/layout/grouped_activities_dialog_group_tab.xml").use { layout ->
|
||||
layout.childNodes.findElementByAttributeValueOrThrow("android:id", "@id/leave_group_button_container")
|
||||
.apply {
|
||||
// Change from "FrameLayout".
|
||||
layout.renameNode(this, namespaceURI, "LinearLayout")
|
||||
|
||||
val leaveButton = childElementsSequence().first()
|
||||
// Get "Leave Group" button ID for bytecode matching.
|
||||
val leaveButtonIdName = leaveButton.getAttribute("android:id").substringAfter('/')
|
||||
leaveIdId = idElements.firstByName(leaveButtonIdName).getAttribute("id").toResourceId()
|
||||
|
||||
// Add surrounding padding to offset decrease on buttons.
|
||||
setAttribute("android:paddingHorizontal", "@dimen/space_2xs")
|
||||
|
||||
// Place buttons next to each other with equal width.
|
||||
val kudosButton = leaveButton.apply {
|
||||
setAttribute("android:layout_width", "0dp")
|
||||
setAttribute("android:layout_weight", "1")
|
||||
// Decrease padding between buttons from "@dimen/button_large_padding" ...
|
||||
setAttribute("android:paddingHorizontal", "@dimen/space_xs")
|
||||
}.cloneNode(true) as Element
|
||||
kudosButton.apply {
|
||||
setAttribute("android:id", "@id/${kudosIdNode.getAttribute("name")}")
|
||||
setAttribute("android:text", "@string/kudos_button")
|
||||
}.let(::appendChild)
|
||||
|
||||
// Downgrade emphasis of "Leave Group" button from "primary".
|
||||
leaveButton.setAttribute("app:emphasis", "secondary")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
val addGiveGroupKudosButtonToGroupActivity = bytecodePatch(
|
||||
name = "Add 'Give Kudos' button to 'Group Activity'",
|
||||
description = "Adds a button that triggers the same action as shaking your phone would."
|
||||
) {
|
||||
compatibleWith("com.strava")
|
||||
|
||||
dependsOn(addGiveKudosButtonToLayoutPatch)
|
||||
|
||||
execute {
|
||||
val className = initFingerprint.originalClassDef.type
|
||||
val onClickListenerClassName = "${className.substringBeforeLast(';')}\$GiveKudosOnClickListener;"
|
||||
|
||||
initFingerprint.method.apply {
|
||||
val constLeaveIdInstruction = instructions.filterIsInstance<BuilderInstruction31i>().first {
|
||||
it.narrowLiteral == leaveIdId
|
||||
}
|
||||
val findViewByIdInstruction =
|
||||
getInstruction<BuilderInstruction35c>(constLeaveIdInstruction.location.index + 1)
|
||||
val moveViewInstruction = getInstruction<BuilderInstruction11x>(constLeaveIdInstruction.location.index + 2)
|
||||
val checkCastButtonInstruction =
|
||||
getInstruction<BuilderInstruction21c>(constLeaveIdInstruction.location.index + 3)
|
||||
|
||||
val buttonClassName = checkCastButtonInstruction.getReference<TypeReference>()!!.type
|
||||
|
||||
addInstructions(
|
||||
constLeaveIdInstruction.location.index,
|
||||
"""
|
||||
${constLeaveIdInstruction.opcode.name} v${constLeaveIdInstruction.registerA}, $kudosIdId
|
||||
${findViewByIdInstruction.opcode.name} { v${findViewByIdInstruction.registerC}, v${findViewByIdInstruction.registerD} }, ${findViewByIdInstruction.reference}
|
||||
${moveViewInstruction.opcode.name} v${moveViewInstruction.registerA}
|
||||
${checkCastButtonInstruction.opcode.name} v${checkCastButtonInstruction.registerA}, ${checkCastButtonInstruction.reference}
|
||||
new-instance v0, $onClickListenerClassName
|
||||
invoke-direct { v0, p0 }, $onClickListenerClassName-><init>($className)V
|
||||
invoke-virtual { p3, v0 }, $buttonClassName->setOnClickListener($ON_CLICK_LISTENER_CLASS_DESCRIPTOR)V
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
val actionHandlerMethod = actionHandlerFingerprint.match(initFingerprint.originalClassDef).method
|
||||
val constShakeToKudosStringIndex = actionHandlerMethod.instructions.indexOfFirst {
|
||||
it is NarrowLiteralInstruction && it.narrowLiteral == shakeToKudosStringId
|
||||
}
|
||||
val getSingletonInstruction = actionHandlerMethod.instructions.filterIsInstance<BuilderInstruction21c>().last {
|
||||
it.opcode == Opcode.SGET_OBJECT && it.location.index < constShakeToKudosStringIndex
|
||||
}
|
||||
|
||||
val outerThisField = ImmutableField(
|
||||
onClickListenerClassName,
|
||||
"outerThis",
|
||||
className,
|
||||
PUBLIC.value or FINAL.value or SYNTHETIC.value,
|
||||
null,
|
||||
listOf(),
|
||||
setOf()
|
||||
)
|
||||
|
||||
val initFieldMethod = ImmutableMethod(
|
||||
onClickListenerClassName,
|
||||
"<init>",
|
||||
listOf(ImmutableMethodParameter(className, setOf(), "outerThis")),
|
||||
"V",
|
||||
PUBLIC.value or SYNTHETIC.value or CONSTRUCTOR.value,
|
||||
setOf(),
|
||||
setOf(),
|
||||
MutableMethodImplementation(2)
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
"""
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
iput-object p1, p0, $outerThisField
|
||||
return-void
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
val onClickMethod = ImmutableMethod(
|
||||
onClickListenerClassName,
|
||||
"onClick",
|
||||
listOf(ImmutableMethodParameter(VIEW_CLASS_DESCRIPTOR, setOf(), "v")),
|
||||
"V",
|
||||
PUBLIC.value or FINAL.value,
|
||||
setOf(),
|
||||
setOf(),
|
||||
MutableMethodImplementation(2)
|
||||
).toMutable().apply {
|
||||
addInstructions(
|
||||
"""
|
||||
sget-object p1, ${getSingletonInstruction.reference}
|
||||
iget-object p0, p0, $outerThisField
|
||||
invoke-virtual { p0, p1 }, ${actionHandlerFingerprint.method}
|
||||
return-void
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
ImmutableClassDef(
|
||||
onClickListenerClassName,
|
||||
PUBLIC.value or FINAL.value or SYNTHETIC.value,
|
||||
"Ljava/lang/Object;",
|
||||
listOf(ON_CLICK_LISTENER_CLASS_DESCRIPTOR),
|
||||
"ProGuard", // Same as source file name of other classes.
|
||||
listOf(),
|
||||
setOf(outerThisField),
|
||||
setOf(initFieldMethod, onClickMethod)
|
||||
).let(classes::add)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package app.revanced.patches.strava.groupkudos
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
|
||||
internal val initFingerprint = fingerprint {
|
||||
parameters("Lcom/strava/feed/view/modal/GroupTabFragment;" , "Z" , "Landroidx/fragment/app/FragmentManager;")
|
||||
custom { method, _ ->
|
||||
method.name == "<init>"
|
||||
}
|
||||
}
|
||||
|
||||
internal val actionHandlerFingerprint = fingerprint {
|
||||
strings("state")
|
||||
}
|
||||
Reference in New Issue
Block a user