Merge pull request #48 from meowarex/dev

Overhaul Patch Defaults <3
This commit is contained in:
2026-06-22 00:17:01 +10:00
committed by GitHub
7 changed files with 76 additions and 65 deletions
@@ -41,6 +41,7 @@ class SmaliPatchStep(
val patches = mutableListOf<LoadedPatch>() val patches = mutableListOf<LoadedPatch>()
val localsBumps = mutableMapOf<Pair<String, String>, Int>() val localsBumps = mutableMapOf<Pair<String, String>, Int>()
val disabledFiles = options.disabledPatchFiles()
// Load and parse all the patches from the smali archive. // Load and parse all the patches from the smali archive.
container.log("Loading patches from smali patch archive: ${patchesZip.absolutePath}") container.log("Loading patches from smali patch archive: ${patchesZip.absolutePath}")
@@ -71,7 +72,7 @@ class SmaliPatchStep(
if (!patchFile.endsWith(".patch")) continue if (!patchFile.endsWith(".patch")) continue
val basename = patchFile.substringAfterLast('/') val basename = patchFile.substringAfterLast('/')
if (basename in options.disabledPatches) { if (basename in disabledFiles) {
container.log("Skipping disabled patch $patchFile") container.log("Skipping disabled patch $patchFile")
continue continue
} }
@@ -2,6 +2,8 @@ package com.meowarex.rlmobile.ui.screens.patchopts
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.meowarex.rlmobile.R import com.meowarex.rlmobile.R
import com.meowarex.rlmobile.ui.screens.patchopts.PatchDefault.Disabled
import com.meowarex.rlmobile.ui.screens.patchopts.PatchDefault.Enabled
data class PatchVariant( data class PatchVariant(
@StringRes val titleRes: Int, @StringRes val titleRes: Int,
@@ -10,30 +12,22 @@ data class PatchVariant(
enum class KnownPatch( enum class KnownPatch(
/** val order: Int, // Patch order in the UI List (lower = higher up) [Main Patches: multiples of 10 | Sub Patches: multiples of 1]
* Numeric display order in the patch options list. Lower = higher up.
*
* Convention: main patches use multiples of 10 (10, 20, 30, …). Patches
* that act as helpers/dependencies of a main patch get offsets adjacent to
* the requirer (e.g. main at 40, helpers at 41, 42, 43). DebugMenuUnlock
* is pinned to 100 to keep it at the bottom of the list.
*/
val order: Int,
val fileNames: List<String>, val fileNames: List<String>,
@StringRes val titleRes: Int, @StringRes val titleRes: Int,
@StringRes val descRes: Int, @StringRes val descRes: Int,
val default: PatchDefault, // Default state of the patch in the UI List (enabled/disabled)
val requires: List<KnownPatch> = emptyList(), val requires: List<KnownPatch> = emptyList(),
val disables: List<KnownPatch> = emptyList(), val disables: List<KnownPatch> = emptyList(),
val variants: List<PatchVariant> = emptyList(), val variants: List<PatchVariant> = emptyList(),
val defaultVariantIndex: Int = 0, val defaultVariantIndex: Int = 0,
) { ) {
// Dependency-first order (later refs need backward resolution).
// The `order` field controls display order; declaration order doesn't matter.
LyricsDisableCover( LyricsDisableCover(
order = 41, order = 41,
fileNames = listOf("lyrics-disable-cover.patch"), fileNames = listOf("lyrics-disable-cover.patch"),
titleRes = R.string.patch_lyrics_disable_cover_title, titleRes = R.string.patch_lyrics_disable_cover_title,
descRes = R.string.patch_lyrics_disable_cover_desc, descRes = R.string.patch_lyrics_disable_cover_desc,
default = Enabled,
), ),
LyricsReplaceLyricsButton( LyricsReplaceLyricsButton(
order = 42, order = 42,
@@ -43,12 +37,14 @@ enum class KnownPatch(
), ),
titleRes = R.string.patch_lyrics_replace_button_title, titleRes = R.string.patch_lyrics_replace_button_title,
descRes = R.string.patch_lyrics_replace_button_desc, descRes = R.string.patch_lyrics_replace_button_desc,
default = Enabled,
), ),
LyricsReplaceShareButton( LyricsReplaceShareButton(
order = 43, order = 43,
fileNames = listOf("lyrics-replace-share-button.patch"), fileNames = listOf("lyrics-replace-share-button.patch"),
titleRes = R.string.patch_lyrics_replace_share_button_title, titleRes = R.string.patch_lyrics_replace_share_button_title,
descRes = R.string.patch_lyrics_replace_share_button_desc, descRes = R.string.patch_lyrics_replace_share_button_desc,
default = Enabled,
), ),
LyricsRlApi( LyricsRlApi(
order = 20, order = 20,
@@ -58,30 +54,35 @@ enum class KnownPatch(
), ),
titleRes = R.string.patch_lyrics_rl_api_title, titleRes = R.string.patch_lyrics_rl_api_title,
descRes = R.string.patch_lyrics_rl_api_desc, descRes = R.string.patch_lyrics_rl_api_desc,
default = Disabled,
), ),
LyricsKeepControlsVisible( LyricsKeepControlsVisible(
order = 60, order = 60,
fileNames = listOf("lyrics-keep-controls-visible.patch"), fileNames = listOf("lyrics-keep-controls-visible.patch"),
titleRes = R.string.patch_lyrics_keep_controls_title, titleRes = R.string.patch_lyrics_keep_controls_title,
descRes = R.string.patch_lyrics_keep_controls_desc, descRes = R.string.patch_lyrics_keep_controls_desc,
default = Enabled,
), ),
PlayerBackdrop( PlayerBackdrop(
order = 30, order = 30,
fileNames = listOf("player-backdrop.patch"), fileNames = listOf("player-backdrop.patch"),
titleRes = R.string.patch_player_backdrop_title, titleRes = R.string.patch_player_backdrop_title,
descRes = R.string.patch_player_backdrop_desc, descRes = R.string.patch_player_backdrop_desc,
default = Enabled,
), ),
QualityBadgeColors( QualityBadgeColors(
order = 36, order = 36,
fileNames = listOf("player-quality-badge-colors.patch"), fileNames = listOf("player-quality-badge-colors.patch"),
titleRes = R.string.patch_quality_badge_colors_title, titleRes = R.string.patch_quality_badge_colors_title,
descRes = R.string.patch_quality_badge_colors_desc, descRes = R.string.patch_quality_badge_colors_desc,
default = Enabled,
), ),
PlayerOneHanded( PlayerOneHanded(
order = 37, order = 37,
fileNames = listOf("player-one-handed.patch"), fileNames = listOf("player-one-handed.patch"),
titleRes = R.string.patch_player_one_handed_title, titleRes = R.string.patch_player_one_handed_title,
descRes = R.string.patch_player_one_handed_desc, descRes = R.string.patch_player_one_handed_desc,
default = Disabled,
), ),
CoverEverywhere( CoverEverywhere(
order = 35, order = 35,
@@ -92,12 +93,14 @@ enum class KnownPatch(
), ),
titleRes = R.string.patch_cover_everywhere_title, titleRes = R.string.patch_cover_everywhere_title,
descRes = R.string.patch_cover_everywhere_desc, descRes = R.string.patch_cover_everywhere_desc,
default = Disabled,
), ),
DebugMenuUnlock( DebugMenuUnlock(
order = 100, order = 100,
fileNames = listOf("debug-menu-unlock.patch"), fileNames = listOf("debug-menu-unlock.patch"),
titleRes = R.string.patch_debug_menu_unlock_title, titleRes = R.string.patch_debug_menu_unlock_title,
descRes = R.string.patch_debug_menu_unlock_desc, descRes = R.string.patch_debug_menu_unlock_desc,
default = Disabled,
), ),
LyricsProgressPill( LyricsProgressPill(
order = 40, order = 40,
@@ -107,6 +110,7 @@ enum class KnownPatch(
), ),
titleRes = R.string.patch_lyrics_progress_pill_title, titleRes = R.string.patch_lyrics_progress_pill_title,
descRes = R.string.patch_lyrics_progress_pill_desc, descRes = R.string.patch_lyrics_progress_pill_desc,
default = Enabled,
requires = listOf(LyricsDisableCover, LyricsReplaceLyricsButton, LyricsReplaceShareButton), requires = listOf(LyricsDisableCover, LyricsReplaceLyricsButton, LyricsReplaceShareButton),
), ),
MiniPlayerRedesign( MiniPlayerRedesign(
@@ -114,6 +118,7 @@ enum class KnownPatch(
fileNames = emptyList(), fileNames = emptyList(),
titleRes = R.string.patch_mini_player_redesign_title, titleRes = R.string.patch_mini_player_redesign_title,
descRes = R.string.patch_mini_player_redesign_desc, descRes = R.string.patch_mini_player_redesign_desc,
default = Disabled,
defaultVariantIndex = 2, defaultVariantIndex = 2,
variants = listOf( variants = listOf(
PatchVariant( PatchVariant(
@@ -135,6 +140,7 @@ enum class KnownPatch(
fileNames = listOf("enable-legacy-ui.patch"), fileNames = listOf("enable-legacy-ui.patch"),
titleRes = R.string.patch_enable_legacy_ui_title, titleRes = R.string.patch_enable_legacy_ui_title,
descRes = R.string.patch_enable_legacy_ui_desc, descRes = R.string.patch_enable_legacy_ui_desc,
default = Disabled,
requires = listOf(DebugMenuUnlock), requires = listOf(DebugMenuUnlock),
disables = listOf( disables = listOf(
LyricsDisableCover, LyricsDisableCover,
@@ -149,14 +155,7 @@ enum class KnownPatch(
), ),
); );
val allVariantFileNames: Set<String>
get() = variants.flatMapTo(mutableSetOf()) { it.fileNames }
companion object { companion object {
/**
* Sorted by `order` ascending. Tie-breaks fall back to the first filename
* (alphabetical) so the order is always deterministic.
*/
val All: List<KnownPatch> = entries.sortedWith( val All: List<KnownPatch> = entries.sortedWith(
compareBy({ it.order }, { it.fileNames.firstOrNull() ?: it.name }) compareBy({ it.order }, { it.fileNames.firstOrNull() ?: it.name })
) )
@@ -0,0 +1,6 @@
package com.meowarex.rlmobile.ui.screens.patchopts
enum class PatchDefault(val isEnabled: Boolean) {
Enabled(true),
Disabled(false),
}
@@ -32,28 +32,34 @@ data class PatchOptions(
*/ */
val customPatches: PatchComponent? = null, val customPatches: PatchComponent? = null,
val disabledPatches: Set<String> = emptySet(), val patchStates: Map<String, Boolean> = emptyMap(),
val selectedVariants: Map<String, Int> = emptyMap(), val selectedVariants: Map<String, Int> = emptyMap(),
) : Parcelable { ) : Parcelable {
companion object {
val Default: PatchOptions = run {
val miniPlayerFiles = KnownPatch.MiniPlayerRedesign.allVariantFileNames
val disabled = (
KnownPatch.DebugMenuUnlock.fileNames +
KnownPatch.EnableLegacyUi.fileNames +
KnownPatch.PlayerOneHanded.fileNames +
miniPlayerFiles
).toSet()
PatchOptions( fun isEnabled(patch: KnownPatch): Boolean =
patchStates[patch.name] ?: patch.default.isEnabled
fun disabledPatchFiles(): Set<String> = buildSet<String> {
for (patch in KnownPatch.All) {
val enabled = isEnabled(patch)
if (patch.variants.isEmpty()) {
if (!enabled) addAll(patch.fileNames)
} else {
val selected = (selectedVariants[patch.name] ?: patch.defaultVariantIndex)
.coerceIn(0, patch.variants.lastIndex)
patch.variants.forEachIndexed { index, variant ->
if (!enabled || index != selected) addAll(variant.fileNames)
}
}
}
}
companion object {
val Default: PatchOptions = PatchOptions(
appName = "TIDAL", appName = "TIDAL",
packageName = "com.aspiro.tidal", packageName = "com.aspiro.tidal",
debuggable = false, debuggable = false,
customTidalApk = null,
customPatches = null,
disabledPatches = disabled,
) )
} }
} }
}
@@ -47,7 +47,7 @@ class PatchOptionsModel(
debuggable = value debuggable = value
} }
var disabledPatches by mutableStateOf(prefilledOptions.disabledPatches) var patchStates by mutableStateOf(prefilledOptions.patchStates)
private set private set
var selectedVariants by mutableStateOf(prefilledOptions.selectedVariants) var selectedVariants by mutableStateOf(prefilledOptions.selectedVariants)
@@ -57,25 +57,10 @@ class PatchOptionsModel(
?.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0)) ?.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0))
?: patch.defaultVariantIndex.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0)) ?: patch.defaultVariantIndex.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0))
fun isPatchEnabled(patch: KnownPatch): Boolean = if (patch.variants.isNotEmpty()) { fun isPatchEnabled(patch: KnownPatch): Boolean =
val v = patch.variants[variantIndex(patch)] patchStates[patch.name] ?: patch.default.isEnabled
v.fileNames.isNotEmpty() && v.fileNames.none { it in disabledPatches }
} else {
patch.fileNames.isNotEmpty() && patch.fileNames.none { it in disabledPatches }
}
fun setPatchEnabled(patch: KnownPatch, enabled: Boolean) { fun setPatchEnabled(patch: KnownPatch, enabled: Boolean) {
if (patch.variants.isNotEmpty()) {
val all = patch.allVariantFileNames
val selected = patch.variants[variantIndex(patch)].fileNames.toSet()
disabledPatches = if (enabled) {
(disabledPatches + all) - selected
} else {
disabledPatches + all
}
return
}
fun closure(seed: KnownPatch, step: (KnownPatch) -> List<KnownPatch>): Set<KnownPatch> = fun closure(seed: KnownPatch, step: (KnownPatch) -> List<KnownPatch>): Set<KnownPatch> =
buildSet { buildSet {
fun walk(p: KnownPatch) { if (add(p)) step(p).forEach(::walk) } fun walk(p: KnownPatch) { if (add(p)) step(p).forEach(::walk) }
@@ -95,16 +80,15 @@ class PatchOptionsModel(
disableUnits = closure(patch) { p -> KnownPatch.All.filter { p in it.requires } } disableUnits = closure(patch) { p -> KnownPatch.All.filter { p in it.requires } }
} }
val enableFiles = enableUnits.flatMap { it.fileNames }.toSet() patchStates = patchStates.toMutableMap().apply {
val disableFiles = disableUnits.flatMap { it.fileNames }.toSet() enableUnits.forEach { this[it.name] = true }
disabledPatches = (disabledPatches - enableFiles) + disableFiles disableUnits.forEach { this[it.name] = false }
}
} }
fun selectVariant(patch: KnownPatch, index: Int) { fun selectVariant(patch: KnownPatch, index: Int) {
if (patch.variants.isEmpty() || index !in patch.variants.indices) return if (patch.variants.isEmpty() || index !in patch.variants.indices) return
val wasOn = isPatchEnabled(patch)
selectedVariants = selectedVariants + (patch.name to index) selectedVariants = selectedVariants + (patch.name to index)
if (wasOn) setPatchEnabled(patch, true)
} }
fun lockState(patch: KnownPatch): PatchLock { fun lockState(patch: KnownPatch): PatchLock {
@@ -175,7 +159,7 @@ class PatchOptionsModel(
debuggable = debuggable, debuggable = debuggable,
customTidalApk = customTidalApk, customTidalApk = customTidalApk,
customPatches = customPatches, customPatches = customPatches,
disabledPatches = disabledPatches, patchStates = patchStates,
selectedVariants = selectedVariants, selectedVariants = selectedVariants,
) )
} }
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"tidalVersionCode": 9090, "tidalVersionCode": 9090,
"tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk", "tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk",
"patchesVersion": "0.9.9" "patchesVersion": "0.9.10"
} }
+18 -3
View File
@@ -1,7 +1,7 @@
# rl-locals: com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali e( 71 # rl-locals: com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali e( 71
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali --- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali +++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
@@ -4172,6 +4172,133 @@ @@ -4172,6 +4172,148 @@
invoke-static {v5, v3, v4}, Landroidx/compose/runtime/Updater;->set-impl(Landroidx/compose/runtime/Composer;Ljava/lang/Object;Lyl0/p;)V invoke-static {v5, v3, v4}, Landroidx/compose/runtime/Updater;->set-impl(Landroidx/compose/runtime/Composer;Ljava/lang/Object;Lyl0/p;)V
@@ -29,9 +29,9 @@
+ +
+ move-result-object v4 # current item + move-result-object v4 # current item
+ +
+ instance-of v6, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a; # only album covers + instance-of v6, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a; # still album cover?
+ +
+ if-eqz v6, :radiant_skip # skip non-albums + if-eqz v6, :radiant_try_animated # if not, try animated album cover
+ +
+ check-cast v4, Lcom/tidal/android/feature/playerscreen/ui/c$a; # narrow type + check-cast v4, Lcom/tidal/android/feature/playerscreen/ui/c$a; # narrow type
+ +
@@ -39,6 +39,21 @@
+ +
+ iget-object v4, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a;->c:Ljava/lang/String; # cover uuid + iget-object v4, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a;->c:Ljava/lang/String; # cover uuid
+ +
+ goto :radiant_have_cover # cover resolved
+
+ :radiant_try_animated # animated album cover (c$b) shares albumId/cover layout
+ instance-of v6, v4, Lcom/tidal/android/feature/playerscreen/ui/c$b; # animated album cover?
+
+ if-eqz v6, :radiant_skip # neither -> skip (music video / unknown)
+
+ check-cast v4, Lcom/tidal/android/feature/playerscreen/ui/c$b; # narrow type
+
+ iget v5, v4, Lcom/tidal/android/feature/playerscreen/ui/c$b;->b:I # album id
+
+ iget-object v4, v4, Lcom/tidal/android/feature/playerscreen/ui/c$b;->c:Ljava/lang/String; # still cover uuid (blur this)
+
+ :radiant_have_cover # v5 = album id, v4 = cover uuid
+
+ new-instance v6, Lcom/tidal/android/feature/playerscreen/ui/composables/n0; # cover request lambda + new-instance v6, Lcom/tidal/android/feature/playerscreen/ui/composables/n0; # cover request lambda
+ +
+ invoke-direct {v6, v5, v4}, Lcom/tidal/android/feature/playerscreen/ui/composables/n0;-><init>(ILjava/lang/String;)V # build request + invoke-direct {v6, v5, v4}, Lcom/tidal/android/feature/playerscreen/ui/composables/n0;-><init>(ILjava/lang/String;)V # build request