feat: remove upgrade button
Release / Release (push) Successful in 56s

This commit is contained in:
2026-08-21 20:49:42 +02:00
parent 6ec6b0032b
commit 723964bc29
3 changed files with 68 additions and 5 deletions
+26
View File
@@ -27,6 +27,32 @@
} }
], ],
"options": [] "options": []
},
{
"name": "Remove timeline Upgrade button",
"description": "Removes the Premium Upgrade button from the timeline's top bar.",
"default": true,
"dependencies": [],
"compatiblePackages": [
{
"packageName": "com.twitter.android",
"name": "X",
"description": null,
"apkFileType": "APKM",
"appIconColor": "#000000",
"signatures": null,
"targets": [
{
"version": "12.7.1-release.0",
"versionCodes": null,
"isExperimental": false,
"minSdk": 28,
"description": null
}
]
}
],
"options": []
} }
] ]
} }
@@ -23,7 +23,7 @@ import kotlin.test.assertTrue
class MorphePatchIntegrationTest { class MorphePatchIntegrationTest {
@Test @Test
fun `applies outdated version patch to target APKM`() { fun `applies patches to target APKM`() {
val inputApkm = requiredPathProperty("integrationTest.inputApkm") val inputApkm = requiredPathProperty("integrationTest.inputApkm")
val patchBundle = requiredPathProperty("integrationTest.patchBundle") val patchBundle = requiredPathProperty("integrationTest.patchBundle")
val morpheCache = requiredPathProperty("integrationTest.morpheCache") val morpheCache = requiredPathProperty("integrationTest.morpheCache")
@@ -47,7 +47,8 @@ class MorphePatchIntegrationTest {
"patch", "patch",
"--patches=${patchBundle.absolutePathString()}", "--patches=${patchBundle.absolutePathString()}",
"--exclusive", "--exclusive",
"--enable=$PATCH_NAME", "--enable=$OUTDATED_PATCH_NAME",
"--enable=$UPGRADE_PATCH_NAME",
"--unsigned", "--unsigned",
"--out=${patchedApk.absolutePathString()}", "--out=${patchedApk.absolutePathString()}",
"--result-file=${resultFile.absolutePathString()}", "--result-file=${resultFile.absolutePathString()}",
@@ -91,7 +92,10 @@ class MorphePatchIntegrationTest {
val appliedPatchNames = result.getAsJsonArray("appliedPatches") val appliedPatchNames = result.getAsJsonArray("appliedPatches")
.map { it.asJsonObject.get("name").asString } .map { it.asJsonObject.get("name").asString }
assertTrue(PATCH_NAME in appliedPatchNames, "Expected patch was not applied: $appliedPatchNames") assertTrue(
appliedPatchNames.containsAll(listOf(OUTDATED_PATCH_NAME, UPGRADE_PATCH_NAME)),
"Expected patches were not applied: $appliedPatchNames"
)
assertTrue(result.getAsJsonArray("failedPatches").isEmpty, "Morphe reported failed patches") assertTrue(result.getAsJsonArray("failedPatches").isEmpty, "Morphe reported failed patches")
} finally { } finally {
workDirectory.toFile().deleteRecursively() workDirectory.toFile().deleteRecursively()
@@ -142,7 +146,8 @@ class MorphePatchIntegrationTest {
} }
private companion object { private companion object {
const val PATCH_NAME = "Disable outdated version screen" const val OUTDATED_PATCH_NAME = "Disable outdated version screen"
const val UPGRADE_PATCH_NAME = "Remove timeline Upgrade button"
const val MORPHE_VERSION = "1.13.1" const val MORPHE_VERSION = "1.13.1"
const val MORPHE_SHA256 = "a212531f68d3ada4b504bf9dac3e02460589f3bf8868ee66e1b4780152d3466b" const val MORPHE_SHA256 = "a212531f68d3ada4b504bf9dac3e02460589f3bf8868ee66e1b4780152d3466b"
const val MORPHE_URL = const val MORPHE_URL =
@@ -0,0 +1,32 @@
package app.twitter.patches
import app.morphe.patcher.patch.resourcePatch
import app.twitter.patches.shared.Constants.COMPATIBILITY_X
import org.w3c.dom.Element
private const val TOOLBAR_ITEMS_PATH = "res/menu/toolbar_items.xml"
private const val UPGRADE_BUTTON_ID = "premium_upsell_upgrade"
@Suppress("unused")
val removeTimelineUpgradeButtonPatch = resourcePatch(
name = "Remove timeline Upgrade button",
description = "Removes the Premium Upgrade button from the timeline's top bar.",
default = true
) {
compatibleWith(COMPATIBILITY_X)
execute {
document(TOOLBAR_ITEMS_PATH).use { document ->
val menu = document.documentElement
val upgradeButton = (0 until menu.childNodes.length)
.asSequence()
.map { menu.childNodes.item(it) }
.filterIsInstance<Element>()
.single {
it.getAttribute("android:id").substringAfterLast('/') == UPGRADE_BUTTON_ID
}
menu.removeChild(upgradeButton)
}
}
}