mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-08-26 22:17:44 +10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fc0241611 | ||
|
|
a648e1a97c | ||
|
|
2150b87371 | ||
|
|
0d82c16168 | ||
|
|
4c257f7ad9 | ||
|
|
7458df8eba | ||
|
|
6a5771cf9e | ||
|
|
a8a576c994 | ||
|
|
585666c403 | ||
|
|
90c008082f | ||
|
|
c2a2586306 | ||
|
|
2291f4064e | ||
|
|
f6a4de0cc7 | ||
|
|
4911e7703d | ||
|
|
f9a174eafa | ||
|
|
49fe4c0d91 | ||
|
|
189e3d28a2 | ||
|
|
aae2f54fab | ||
|
|
191dcca239 | ||
|
|
08aee155c9 | ||
|
|
47c9c37236 | ||
|
|
50b99b6193 | ||
|
|
03271bf26d | ||
|
|
75f8187526 | ||
|
|
523f0a342b | ||
|
|
fb4c776c61 | ||
|
|
cbf04d9fa7 | ||
|
|
266cd184dd | ||
|
|
04ea66acdb | ||
|
|
22526f4e0b | ||
|
|
5a3ad2df7a | ||
|
|
fa99d10134 | ||
|
|
4e8af5960c | ||
|
|
09f6e8d34f | ||
|
|
160be11ac4 | ||
|
|
4486e52117 | ||
|
|
bafd40a20c | ||
|
|
c89827c7b2 | ||
|
|
90b49cc571 | ||
|
|
8e86c0d2e1 | ||
|
|
8ac9fb0db5 | ||
|
|
6ad6cb2fe0 | ||
|
|
a9611a1667 | ||
|
|
276dd0be2e | ||
|
|
3758ba19f3 | ||
|
|
75450aad60 | ||
|
|
3788eb2008 | ||
|
|
95be099821 | ||
|
|
7a55787894 | ||
|
|
bd09c2798e | ||
|
|
b0b614dc6f | ||
|
|
6a7d5fe90f | ||
|
|
4791e69bec | ||
|
|
7f3618aeae | ||
|
|
ea9c6458c2 | ||
|
|
bdbcc4fc62 | ||
|
|
3b893c12ff | ||
|
|
9d60e3dc80 | ||
|
|
36382437f1 | ||
|
|
5dc9188772 | ||
|
|
7823a54d11 | ||
|
|
4d5fea843e | ||
|
|
d6bc889387 | ||
|
|
8501ccb0a9 | ||
|
|
ad6ebac6ce | ||
|
|
320148e774 | ||
|
|
d8e119eb4b | ||
|
|
10e5dcf365 | ||
|
|
1c65c5669a | ||
|
|
961291166c | ||
|
|
25852aaea1 | ||
|
|
c6b18d1aa9 | ||
|
|
ee8b25330d | ||
|
|
193d5ad90d | ||
|
|
942742e86e | ||
|
|
0b039110c9 | ||
|
|
045bbbab9a | ||
|
|
932f2a85dc | ||
|
|
fc002615f4 | ||
|
|
c89dd54fa6 | ||
|
|
53a4341d6b | ||
|
|
c81d2edba3 | ||
|
|
9180129afc |
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -228,3 +228,13 @@ fun ProviderFactory.execIgnoreCode(vararg command: String): String = try {
|
||||
} catch (_: Exception) {
|
||||
""
|
||||
}
|
||||
|
||||
// Sync Local Cached Assets with Repo Releases to keep them up to date
|
||||
val syncBundledManifest by tasks.registering(Copy::class) {
|
||||
val src = rootProject.rootDir.resolve("../patches/manifest.json")
|
||||
onlyIf { src.exists() }
|
||||
from(src)
|
||||
into(layout.projectDirectory.dir("src/main/assets"))
|
||||
rename { "patches-manifest.json" }
|
||||
}
|
||||
tasks.named("preBuild") { dependsOn(syncBundledManifest) }
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
{
|
||||
"version": 1,
|
||||
"patches": [
|
||||
{
|
||||
"id": "EnableLegacyUi",
|
||||
"order": 10,
|
||||
"fileNames": [
|
||||
"enable-legacy-ui.patch"
|
||||
],
|
||||
"title": "Enable Legacy UI",
|
||||
"description": "[This patch will stop working soon] - Replaces the New Compose based UI with the Legacy UI (disables player-market-ui feature flag)",
|
||||
"default": false,
|
||||
"requires": [
|
||||
"DebugMenuUnlock"
|
||||
],
|
||||
"disables": [
|
||||
"LyricsDisableCover",
|
||||
"LyricsReplaceLyricsButton",
|
||||
"LyricsReplaceShareButton",
|
||||
"LyricsRlApi",
|
||||
"LyricsKeepControlsVisible",
|
||||
"PlayerBackdrop",
|
||||
"QualityBadgeColors",
|
||||
"PlayerMovePlayingFrom",
|
||||
"PlayerOneHanded",
|
||||
"LyricsProgressPill",
|
||||
"MiniPlayerRedesign"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "LyricsRlApi",
|
||||
"order": 20,
|
||||
"fileNames": [
|
||||
"lyrics-rl-api.patch",
|
||||
"lyrics-rl-api-observer.patch",
|
||||
"lyrics-rl-api-isrc.patch"
|
||||
],
|
||||
"title": "Radiant Lyrics API - WIP",
|
||||
"description": "Use Radiant Lyrics API to fetch Lyrics (Syllable Level & Higher Quality)",
|
||||
"default": false,
|
||||
"defaultVariantIndex": 0,
|
||||
"variants": [
|
||||
{
|
||||
"title": "Line",
|
||||
"fileNames": []
|
||||
},
|
||||
{
|
||||
"title": "Word",
|
||||
"fileNames": [
|
||||
"lyrics-rl-api-word.patch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Syllable",
|
||||
"fileNames": [
|
||||
"lyrics-rl-api-syllable.patch"
|
||||
]
|
||||
}
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/RLAPILyricsHook.smali",
|
||||
"radiant/RLAPILyricsWorker.smali",
|
||||
"radiant/IsrcCache.smali",
|
||||
"radiant/WordLyrics.smali",
|
||||
"radiant/WordLyricsTick.smali",
|
||||
"radiant/SylBrush.smali",
|
||||
"radiant/SylLayout.smali"
|
||||
],
|
||||
"advancedOptions": [
|
||||
{
|
||||
"type": "toggle",
|
||||
"key": "romanize",
|
||||
"title": "Romanization",
|
||||
"description": "Return a romanized transcription and show that instead of the original script",
|
||||
"default": false,
|
||||
"token": "RL_ROMANIZE",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"type": "toggle",
|
||||
"key": "context_aware",
|
||||
"title": "Context Aware Lyrics",
|
||||
"description": "Display voice context cues (adlibs and duet singers get displayed seperately)",
|
||||
"default": true,
|
||||
"token": "RL_CTX_ON"
|
||||
},
|
||||
{
|
||||
"type": "choice",
|
||||
"key": "context_aware_mode",
|
||||
"title": "Show",
|
||||
"description": "",
|
||||
"entries": [
|
||||
"Adlibs",
|
||||
"Singers",
|
||||
"Both"
|
||||
],
|
||||
"values": [
|
||||
"0x1",
|
||||
"0x2",
|
||||
"0x3"
|
||||
],
|
||||
"defaultIndex": 0,
|
||||
"requiresOption": "context_aware",
|
||||
"token": "RL_CTX"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "PlayerBackdrop",
|
||||
"order": 30,
|
||||
"fileNames": [],
|
||||
"title": "Player Backdrop",
|
||||
"description": "Lets you fully adjust how the player backdrop blur looks and behaves",
|
||||
"default": true,
|
||||
"defaultVariantIndex": 2,
|
||||
"variants": [
|
||||
{
|
||||
"title": "Static",
|
||||
"fileNames": [
|
||||
"player-backdrop-static.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/BackdropFx.smali"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Animated",
|
||||
"fileNames": [
|
||||
"player-backdrop-animated.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/BackdropFx.smali"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Playback",
|
||||
"fileNames": [
|
||||
"player-backdrop-playback.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/BackdropFx.smali"
|
||||
]
|
||||
}
|
||||
],
|
||||
"advancedOptions": [
|
||||
{
|
||||
"type": "slider",
|
||||
"key": "blur_strength",
|
||||
"title": "Blur Strength",
|
||||
"description": "The value of Blur applied to the backdrop cover",
|
||||
"default": 50,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"steps": 19,
|
||||
"displayAsPercent": true,
|
||||
"token": "RL_BLUR",
|
||||
"encode": {
|
||||
"kind": "int",
|
||||
"scale": 1.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "slider",
|
||||
"key": "contrast",
|
||||
"title": "Contrast",
|
||||
"description": "The value of Contrast applied to the backdrop cover",
|
||||
"default": 50,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"steps": 19,
|
||||
"displayAsPercent": true,
|
||||
"token": "RL_CONTRAST",
|
||||
"encode": {
|
||||
"kind": "floatBits",
|
||||
"scale": 0.02
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "slider",
|
||||
"key": "saturation",
|
||||
"title": "Saturation",
|
||||
"description": "The value of Saturation applied to the backdrop cover",
|
||||
"default": 50,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"steps": 19,
|
||||
"displayAsPercent": true,
|
||||
"token": "RL_SATURATION",
|
||||
"encode": {
|
||||
"kind": "floatBits",
|
||||
"scale": 0.02
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "slider",
|
||||
"key": "brightness",
|
||||
"title": "Brightness",
|
||||
"description": "The value of Brightness applied to the backdrop cover",
|
||||
"default": 50,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"steps": 19,
|
||||
"displayAsPercent": true,
|
||||
"token": "RL_BRIGHTNESS",
|
||||
"encode": {
|
||||
"kind": "floatBits",
|
||||
"scale": 0.02
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "toggle",
|
||||
"key": "cover_everywhere",
|
||||
"title": "Cover Everywhere - WIP",
|
||||
"description": "Applies TIDAL's native blurred album-art backdrop to every Compose based page (Home & Collection)",
|
||||
"default": false,
|
||||
"inline": true,
|
||||
"fileNames": [
|
||||
"home-backdrop.patch",
|
||||
"collection-backdrop.patch",
|
||||
"cover-capture.patch"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "QualityBadgeColors",
|
||||
"order": 36,
|
||||
"fileNames": [
|
||||
"player-quality-badge-colors.patch"
|
||||
],
|
||||
"title": "Quality Badge Colors",
|
||||
"description": "Brings color back to the player quality badge.",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"id": "PlayerMovePlayingFrom",
|
||||
"order": 37,
|
||||
"fileNames": [
|
||||
"player-move-playing-from.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/PlayingFrom.smali"
|
||||
],
|
||||
"title": "Playing From",
|
||||
"description": "Shows the source name (playlist/album/mix) above the track title",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"id": "PlayerOneHanded",
|
||||
"order": 38,
|
||||
"fileNames": [
|
||||
"player-one-handed.patch"
|
||||
],
|
||||
"title": "One-Handed Mode",
|
||||
"description": "Mirrors the bottom player buttons left-to-right for easier one-handed reach.",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"id": "LyricsProgressPill",
|
||||
"order": 40,
|
||||
"fileNames": [
|
||||
"lyrics-progress-pill.patch",
|
||||
"lyrics-fade-region.patch"
|
||||
],
|
||||
"title": "Lyrics Progress Pill",
|
||||
"description": "Restores the old Track Progress Pill in the top of the Lyrics screen!",
|
||||
"default": true,
|
||||
"requires": [
|
||||
"LyricsDisableCover",
|
||||
"LyricsReplaceLyricsButton",
|
||||
"LyricsReplaceShareButton"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "LyricsDisableCover",
|
||||
"order": 41,
|
||||
"fileNames": [
|
||||
"lyrics-disable-cover.patch"
|
||||
],
|
||||
"title": "Disable Lyrics Cover",
|
||||
"description": "Removes the mini track cover from the lyrics screen.",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"id": "LyricsReplaceLyricsButton",
|
||||
"order": 42,
|
||||
"fileNames": [
|
||||
"lyrics-replace-lyrics-button.patch",
|
||||
"lyrics-sparkle-conditional-visibility.patch"
|
||||
],
|
||||
"title": "Replace Lyrics Button",
|
||||
"description": "Replaces the Lyrics button with the RL Sparkle!",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"id": "LyricsReplaceShareButton",
|
||||
"order": 43,
|
||||
"fileNames": [
|
||||
"lyrics-replace-share-button.patch"
|
||||
],
|
||||
"title": "Replace Share Button",
|
||||
"description": "Replaces the share button with the Tidal Connect button from the top of the screen. (it's in the menu already)",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"id": "MiniPlayerRedesign",
|
||||
"order": 50,
|
||||
"fileNames": [],
|
||||
"title": "Redesigned Mini-Player",
|
||||
"description": "Integrates the Seekbar into the control panel at the bottom of the screen.",
|
||||
"default": false,
|
||||
"defaultVariantIndex": 2,
|
||||
"variants": [
|
||||
{
|
||||
"title": "Floating",
|
||||
"fileNames": []
|
||||
},
|
||||
{
|
||||
"title": "Grey",
|
||||
"fileNames": [
|
||||
"mini-player-grey.patch"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Black",
|
||||
"fileNames": [
|
||||
"mini-player-black.patch"
|
||||
]
|
||||
}
|
||||
],
|
||||
"advancedOptions": [
|
||||
{
|
||||
"type": "toggle",
|
||||
"key": "dynamic_bg",
|
||||
"title": "Mini-Player Dynamic Background",
|
||||
"description": "Colorizes the Mini-Player depending on the currently playing track.",
|
||||
"default": false,
|
||||
"inline": true,
|
||||
"fileNames": [
|
||||
"mini-player-dynamic-bg.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/MiniPlayerBackground.smali"
|
||||
],
|
||||
"hidesVariants": [
|
||||
2
|
||||
],
|
||||
"relabelVariants": {
|
||||
"1": "Legacy"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "toggle",
|
||||
"key": "border",
|
||||
"title": "Progress Border",
|
||||
"description": "Draws an animated progress ring around the floating mini-player. Requires the Floating style.",
|
||||
"default": false,
|
||||
"requiresVariant": 0,
|
||||
"fileNames": [
|
||||
"mini-player-floating-border.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/MiniSeekerFloating.smali"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "toggle",
|
||||
"key": "gestures",
|
||||
"title": "Mini-Player Gestures",
|
||||
"description": "Adds swipe up gesture to the mini-player to reopen the player",
|
||||
"default": true,
|
||||
"fileNames": [
|
||||
"mini-player-gestures.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/MiniPlayerGestures.smali",
|
||||
"radiant/MiniPlayerGestures$Gesture.smali",
|
||||
"radiant/MiniPlayerGestures$FeedbackLayer.smali",
|
||||
"radiant/MiniPlayerGestures$FeedbackResetAnimator.smali",
|
||||
"radiant/MiniPlayerGestures$RootGesture.smali",
|
||||
"radiant/MiniPlayerGestures$ApplyPending.smali"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "choice",
|
||||
"key": "swipe_up_drag",
|
||||
"title": "Swipe Up Style",
|
||||
"entries": [
|
||||
"Static",
|
||||
"Drag"
|
||||
],
|
||||
"values": [
|
||||
"0x0",
|
||||
"0x1"
|
||||
],
|
||||
"defaultIndex": 1,
|
||||
"requiresOption": "gestures",
|
||||
"token": "RL_MINI_PLAYER_SWIPE_UP_DRAG"
|
||||
},
|
||||
{
|
||||
"type": "toggle",
|
||||
"key": "next_prev",
|
||||
"title": "Next/Previous Gestures",
|
||||
"description": "Swipe left or right on the mini-player to skip tracks.",
|
||||
"default": false,
|
||||
"requiresOption": "gestures",
|
||||
"fileNames": [
|
||||
"mini-player-gestures-left-right.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/MiniPlayerTrackGestures.smali",
|
||||
"radiant/MiniPlayerTrackGestures$Gesture.smali",
|
||||
"radiant/MiniPlayerTrackGestures$OffsetLayer.smali",
|
||||
"radiant/MiniPlayerTrackGestures$ResetAnimator.smali",
|
||||
"radiant/MiniPlayerTrackGestures$TextDraw.smali",
|
||||
"com/tidal/android/feature/appscaffold/ui/r$c.smali"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "LyricsKeepControlsVisible",
|
||||
"order": 60,
|
||||
"fileNames": [
|
||||
"lyrics-keep-controls-visible.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/LyricsReinitGuard.smali"
|
||||
],
|
||||
"title": "Keep Controls Visible",
|
||||
"description": "Inverts the auto-hide behavior on the lyrics screen, playback controls stay visible by default and only hide when you tap them off. Also stops the open lyrics view from randomly closing when the player re-fetches the same track.",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"id": "WazeIntegration",
|
||||
"order": 90,
|
||||
"category": "integration",
|
||||
"pathLocked": true,
|
||||
"fileNames": [
|
||||
"integration-waze-playback.patch"
|
||||
],
|
||||
"extensionFiles": [
|
||||
"radiant/WazeInitReceiver.smali",
|
||||
"radiant/WazeServiceConnection.smali"
|
||||
],
|
||||
"title": "Waze Integration",
|
||||
"description": "Control TIDAL playback from the Waze app",
|
||||
"default": true,
|
||||
"advancedOptions": [
|
||||
{
|
||||
"type": "choice",
|
||||
"key": "browse_root",
|
||||
"title": "Media Menu",
|
||||
"entries": [
|
||||
"Auto Menu",
|
||||
"Home",
|
||||
"Recents"
|
||||
],
|
||||
"values": [
|
||||
"ROOT_AUTO",
|
||||
"HOME_V2::home_page_v2_id",
|
||||
"RECENTLY_PLAYED::home/pages/CONTINUE_LISTEN_TO/view-all"
|
||||
],
|
||||
"defaultIndex": 0,
|
||||
"token": "RL_WAZE_ROOT_ID"
|
||||
},
|
||||
{
|
||||
"type": "color",
|
||||
"key": "accent_color",
|
||||
"title": "Accent Color",
|
||||
"default": -16747037,
|
||||
"token": "RL_WAZE_THEME_COLOR"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "DebugMenuUnlock",
|
||||
"order": 100,
|
||||
"fileNames": [
|
||||
"debug-menu-unlock.patch"
|
||||
],
|
||||
"title": "Unlock Debug Menu",
|
||||
"description": "Reveals TIDAL's hidden Debug Menu in Settings. (Unlocks Feature Flags & the legacy Debug Options)",
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import com.meowarex.rlmobile.ui.screens.about.AboutModel
|
||||
import com.meowarex.rlmobile.ui.screens.componentopts.ComponentOptionsModel
|
||||
import com.meowarex.rlmobile.ui.screens.home.HomeModel
|
||||
import com.meowarex.rlmobile.ui.screens.log.LogScreenModel
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogsListScreenModel
|
||||
import com.meowarex.rlmobile.ui.screens.patching.PatchingScreenModel
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionsModel
|
||||
import com.meowarex.rlmobile.ui.screens.permissions.PermissionsModel
|
||||
@@ -64,7 +63,6 @@ class ManagerApplication : Application() {
|
||||
factoryOf(::PatchOptionsModel)
|
||||
factoryOf(::ComponentOptionsModel)
|
||||
factoryOf(::LogScreenModel)
|
||||
factoryOf(::LogsListScreenModel)
|
||||
factoryOf(::PermissionsModel)
|
||||
viewModelOf(::UpdaterViewModel)
|
||||
})
|
||||
|
||||
@@ -12,8 +12,6 @@ import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
@@ -21,52 +19,19 @@ import kotlin.time.Duration
|
||||
import kotlin.time.Instant
|
||||
|
||||
/**
|
||||
* Central manager for storing all attempted installations and
|
||||
* their associated logs/crashes (not including manager crashes themselves).
|
||||
* In-memory store for the most recent install attempts
|
||||
*/
|
||||
class InstallLogManager(
|
||||
private val application: Application,
|
||||
private val prefs: PreferencesManager,
|
||||
private val json: Json,
|
||||
@Suppress("unused") private val json: Json,
|
||||
) {
|
||||
val logsDir = application.filesDir.resolve("install-logs").apply { mkdir() }
|
||||
private val entries = linkedMapOf<String, InstallLogData>()
|
||||
|
||||
/**
|
||||
* Lists all the install data entries that exist on disk, sorted decreasing by
|
||||
* the file creation date.
|
||||
* @return List of installation ids, most recent installation first.
|
||||
*/
|
||||
fun fetchInstallDataEntries(): List<String> {
|
||||
val files = logsDir.listFiles { it.extension == "json" } ?: emptyArray()
|
||||
|
||||
return files
|
||||
.sortedByDescending { it.lastModified() }
|
||||
.map { it.nameWithoutExtension }
|
||||
fun fetchInstallData(id: String): InstallLogData? = synchronized(entries) {
|
||||
entries[id]
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the install log from disk, if it exists.
|
||||
*/
|
||||
fun fetchInstallData(id: String): InstallLogData? {
|
||||
val path = logsDir.resolve("$id.json")
|
||||
if (!path.exists()) return null
|
||||
|
||||
return try {
|
||||
json.decodeFromStream(path.inputStream())
|
||||
} catch (t: Throwable) {
|
||||
Log.e(BuildConfig.TAG, "Failed to open install log $id", t)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteAllEntries() {
|
||||
logsDir.deleteRecursively()
|
||||
logsDir.mkdir()
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an install log entry to disk.
|
||||
*/
|
||||
suspend fun storeInstallData(
|
||||
id: String,
|
||||
installDate: Instant,
|
||||
@@ -75,8 +40,6 @@ class InstallLogManager(
|
||||
log: String,
|
||||
error: Throwable?,
|
||||
) {
|
||||
val path = logsDir.resolve("$id.json")
|
||||
|
||||
val data = InstallLogData(
|
||||
id = id,
|
||||
installDate = installDate,
|
||||
@@ -87,16 +50,16 @@ class InstallLogManager(
|
||||
errorStacktrace = error?.let { Log.getStackTraceString(it).trimEnd() },
|
||||
)
|
||||
|
||||
try {
|
||||
path.writeText(json.encodeToString(data))
|
||||
} catch (e: IOException) {
|
||||
Log.e(BuildConfig.TAG, "Failed to write log to disk", e)
|
||||
synchronized(entries) {
|
||||
entries[id] = data
|
||||
// Keep only the most recent few in memory.
|
||||
while (entries.size > MAX_ENTRIES) {
|
||||
val oldest = entries.keys.iterator().next()
|
||||
entries.remove(oldest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of details about the current installation environment.
|
||||
*/
|
||||
@Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants")
|
||||
@SuppressLint("UsableSpace")
|
||||
suspend fun getEnvironmentInfo(): String {
|
||||
@@ -139,6 +102,10 @@ class InstallLogManager(
|
||||
SOC: $soc
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MAX_ENTRIES = 8
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
|
||||
@@ -22,7 +22,8 @@ class PathManager(
|
||||
|
||||
val patchingDownloadDir = patchingDir.resolve("downloads")
|
||||
|
||||
val cacheDownloadDir = context.cacheDir.resolve("downloads")
|
||||
// Persistent Stash of assets for Offline Repatching
|
||||
val cacheDownloadDir = patchingDir.resolve("downloads-cache")
|
||||
|
||||
val customComponentsDir = patchingDir.resolve("custom")
|
||||
|
||||
@@ -34,11 +35,15 @@ class PathManager(
|
||||
|
||||
val patchedApk = patchingWorkingDir.resolve("patched.apk")
|
||||
|
||||
// Persistent snapshot of the last successful release fetch (build info + patches asset URL)
|
||||
val cachedReleaseInfo = patchingDir.resolve("release-info.json")
|
||||
|
||||
fun clearCache() {
|
||||
val targets = arrayOf(
|
||||
patchingDownloadDir,
|
||||
patchingWorkingDir,
|
||||
cacheDownloadDir,
|
||||
cachedReleaseInfo,
|
||||
context.cacheDir,
|
||||
)
|
||||
for (dir in targets) {
|
||||
@@ -54,6 +59,16 @@ class PathManager(
|
||||
.resolve("patches")
|
||||
.resolve("$version.zip")
|
||||
|
||||
fun hasCachedTidalApk(version: Int, split: String = "base"): Boolean {
|
||||
val rel = "tidal/$version/$split.apk"
|
||||
return patchingDownloadDir.resolve(rel).exists() || cacheDownloadDir.resolve(rel).exists()
|
||||
}
|
||||
|
||||
fun hasCachedSmaliPatches(version: SemVer): Boolean {
|
||||
val rel = "patches/$version.zip"
|
||||
return patchingDownloadDir.resolve(rel).exists() || cacheDownloadDir.resolve(rel).exists()
|
||||
}
|
||||
|
||||
fun customTidalApks() = customTidalApksDir.listFiles()?.asList() ?: emptyList()
|
||||
|
||||
fun customSmaliPatches() = customPatchesDir.listFiles()?.asList() ?: emptyList()
|
||||
|
||||
@@ -12,6 +12,8 @@ class PreferencesManager(preferences: SharedPreferences) : BasePreferenceManager
|
||||
var devMode by booleanPreference("dev_mode", false)
|
||||
var installer by enumPreference<InstallerSetting>("installer", InstallerSetting.PackageInstaller)
|
||||
var keepPatchedApks by booleanPreference("keep_patched_apks", false)
|
||||
// Dev: force-apply path-gated ("incompatible") patches even under a non-stock package name
|
||||
var bypassIncompatible by booleanPreference("bypass_incompatible", false)
|
||||
var showPlayProtectWarning by booleanPreference("show_play_protect_warning", true)
|
||||
var autoUpdateCheck by booleanPreference("auto_update_check", true)
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.meowarex.rlmobile.manager
|
||||
|
||||
import android.util.Log
|
||||
import com.meowarex.rlmobile.BuildConfig
|
||||
import com.meowarex.rlmobile.network.models.RLBuildInfo
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
// Snapshot of the last successful release fetch (build info + patches asset URL)
|
||||
@Serializable
|
||||
data class CachedReleaseInfo(
|
||||
val data: RLBuildInfo,
|
||||
val patchesAssetUrl: String,
|
||||
)
|
||||
|
||||
object ReleaseInfoCache {
|
||||
fun load(paths: PathManager, json: Json): CachedReleaseInfo? = try {
|
||||
paths.cachedReleaseInfo
|
||||
.takeIf { it.exists() }
|
||||
?.let { json.decodeFromString<CachedReleaseInfo>(it.readText()) }
|
||||
} catch (t: Throwable) {
|
||||
Log.w(BuildConfig.TAG, "Failed to read cached release info", t)
|
||||
null
|
||||
}
|
||||
|
||||
fun save(paths: PathManager, json: Json, info: CachedReleaseInfo) = try {
|
||||
paths.cachedReleaseInfo.parentFile?.mkdirs()
|
||||
paths.cachedReleaseInfo.writeText(json.encodeToString(info))
|
||||
} catch (t: Throwable) {
|
||||
Log.w(BuildConfig.TAG, "Failed to cache release info", t)
|
||||
}
|
||||
}
|
||||
+5
@@ -26,11 +26,16 @@ class PatchManifestStep(private val options: PatchOptions) : Step() {
|
||||
?: throw IllegalArgumentException("No manifest found in APK")
|
||||
|
||||
container.log("Patching manifest")
|
||||
val wazeDefault = container.getStep<SmaliPatchStep>().specs
|
||||
.firstOrNull { it.id == "WazeIntegration" }?.defaultEnabled ?: false
|
||||
val enableWazeIntegration = options.patchStates["WazeIntegration"] ?: wazeDefault
|
||||
|
||||
val patchedManifest = ManifestPatcher.patchManifest(
|
||||
manifestBytes = manifest,
|
||||
packageName = options.packageName,
|
||||
appName = options.appName,
|
||||
debuggable = options.debuggable,
|
||||
enableWazeIntegration = enableWazeIntegration,
|
||||
)
|
||||
|
||||
container.log("Repacking apk with patched manifest")
|
||||
|
||||
+119
-16
@@ -1,5 +1,6 @@
|
||||
package com.meowarex.rlmobile.patcher.steps.patch
|
||||
|
||||
import android.content.Context
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.manager.PathManager
|
||||
import com.meowarex.rlmobile.patcher.StepRunner
|
||||
@@ -8,7 +9,10 @@ import com.meowarex.rlmobile.patcher.steps.base.IDexProvider
|
||||
import com.meowarex.rlmobile.patcher.steps.base.Step
|
||||
import com.meowarex.rlmobile.patcher.steps.download.CopyDependenciesStep
|
||||
import com.meowarex.rlmobile.patcher.steps.download.DownloadPatchesStep
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchManifest
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchSpec
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.builtinPatchSpecs
|
||||
import com.android.tools.smali.baksmali.Baksmali
|
||||
import com.android.tools.smali.baksmali.BaksmaliOptions
|
||||
import com.android.tools.smali.dexlib2.Opcodes
|
||||
@@ -19,6 +23,7 @@ import com.github.diamondminer88.zip.ZipReader
|
||||
import com.github.difflib.DiffUtils
|
||||
import com.github.difflib.UnifiedDiffUtils
|
||||
import com.github.difflib.patch.Patch
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import java.io.*
|
||||
@@ -27,6 +32,11 @@ class SmaliPatchStep(
|
||||
private val options: PatchOptions,
|
||||
) : Step(), IDexProvider, KoinComponent {
|
||||
private val paths: PathManager by inject()
|
||||
private val json: Json by inject()
|
||||
private val context: Context by inject()
|
||||
|
||||
var specs: List<PatchSpec> = emptyList()
|
||||
private set
|
||||
|
||||
override val group = StepGroup.Patch
|
||||
override val localizedName = R.string.patch_step_patch_smali
|
||||
@@ -42,6 +52,29 @@ class SmaliPatchStep(
|
||||
val patches = mutableListOf<LoadedPatch>()
|
||||
val localsBumps = mutableMapOf<Pair<String, String>, Int>()
|
||||
|
||||
// The patch list/options metadata lives in the zip's manifest.json
|
||||
val manifestSpecs = try {
|
||||
ZipReader(patchesZip).use { it.openEntry("manifest.json")?.read() }
|
||||
?.let { json.decodeFromString(PatchManifest.serializer(), it.decodeToString()).patches }
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
} catch (t: Throwable) {
|
||||
container.log("Failed to parse manifest.json (${t.message}); using built-in patch specs")
|
||||
null
|
||||
}
|
||||
specs = manifestSpecs ?: builtinPatchSpecs(context, json)
|
||||
container.log(
|
||||
"Loaded ${specs.size} patch specs from " +
|
||||
if (manifestSpecs != null) "manifest.json" else "built-in list"
|
||||
)
|
||||
|
||||
val disabledFiles = options.disabledPatchFiles(specs)
|
||||
val knownExtensionFiles = options.knownExtensionFiles(specs)
|
||||
val enabledExtensionFiles = options.enabledExtensionFiles(specs)
|
||||
val substitutions = options.smaliSubstitutions(specs)
|
||||
if (substitutions.isNotEmpty()) {
|
||||
container.log("Patch option substitutions: ${substitutions.entries.joinToString { "${it.key}=${it.value}" }}")
|
||||
}
|
||||
|
||||
// Load and parse all the patches from the smali archive.
|
||||
container.log("Loading patches from smali patch archive: ${patchesZip.absolutePath}")
|
||||
smaliDir.mkdirs()
|
||||
@@ -53,6 +86,11 @@ class SmaliPatchStep(
|
||||
|
||||
if (patchFile.endsWith(".smali") && patchFile.startsWith("extension/")) {
|
||||
val relative = patchFile.removePrefix("extension/")
|
||||
// Only bundle helper smali patch/variant/sub-option is enabled
|
||||
if (relative in knownExtensionFiles && relative !in enabledExtensionFiles) {
|
||||
container.log("Skipping disabled extension smali: $relative")
|
||||
continue
|
||||
}
|
||||
val out = smaliDir.resolve(relative)
|
||||
// Guard against zip-slip: a crafted entry could otherwise escape smaliDir.
|
||||
val baseCanonical = smaliDir.canonicalPath + File.separator
|
||||
@@ -63,7 +101,19 @@ class SmaliPatchStep(
|
||||
val entry = zip.openEntry(patchFile)
|
||||
?: throw FileNotFoundException("Missing zip entry: $patchFile")
|
||||
out.canonicalFile.parentFile?.mkdirs()
|
||||
out.writeBytes(entry.read())
|
||||
var smaliText = entry.read()
|
||||
.decodeToString()
|
||||
.replace("\r\n", "\n")
|
||||
for ((token, value) in substitutions) {
|
||||
if (smaliText.contains(token)) {
|
||||
smaliText = smaliText.replace(token, value)
|
||||
container.log("Applied substitution $token -> $value in $patchFile")
|
||||
}
|
||||
}
|
||||
UNRESOLVED_TOKEN.find(smaliText)?.let { match ->
|
||||
throw Error("Unresolved option placeholder ${match.value} in $patchFile")
|
||||
}
|
||||
out.writeText(smaliText)
|
||||
container.log("Extracted extension smali: $relative")
|
||||
continue
|
||||
}
|
||||
@@ -71,16 +121,39 @@ class SmaliPatchStep(
|
||||
if (!patchFile.endsWith(".patch")) continue
|
||||
|
||||
val basename = patchFile.substringAfterLast('/')
|
||||
if (basename in options.disabledPatches) {
|
||||
if (basename in disabledFiles) {
|
||||
container.log("Skipping disabled patch $patchFile")
|
||||
continue
|
||||
}
|
||||
|
||||
val lines = zip.openEntry(patchFile)!!.read()
|
||||
var patchText = zip.openEntry(patchFile)!!.read()
|
||||
.decodeToString()
|
||||
.replace("\r\n", "\n") // Replace CRLF endings with LF
|
||||
.trimEnd { it == '\n' } // Remove trailing new lines
|
||||
.split('\n')
|
||||
|
||||
// Bake advanced option values into the patch
|
||||
for ((token, value) in substitutions) {
|
||||
if (patchText.contains(token)) {
|
||||
patchText = patchText.replace(token, value)
|
||||
container.log("Applied substitution $token -> $value in $patchFile")
|
||||
}
|
||||
}
|
||||
|
||||
// Fail fast
|
||||
UNRESOLVED_TOKEN.find(patchText)?.let { match ->
|
||||
throw Error("Unresolved option placeholder ${match.value} in $patchFile")
|
||||
}
|
||||
|
||||
// Skip a patch that references a helper class which won't be bundled
|
||||
val missingHelper = (knownExtensionFiles - enabledExtensionFiles).firstOrNull { rel ->
|
||||
patchText.contains("L${rel.removeSuffix(".smali")};")
|
||||
}
|
||||
if (missingHelper != null) {
|
||||
container.log("Skipping $patchFile: references gated-off helper class $missingHelper")
|
||||
continue
|
||||
}
|
||||
|
||||
val lines = patchText.split('\n')
|
||||
|
||||
try {
|
||||
for (directive in lines) {
|
||||
@@ -92,18 +165,25 @@ class SmaliPatchStep(
|
||||
container.log("Recorded rl-locals bump: $smaliPath method≈\"$methodSubstring\" >= $newValue")
|
||||
}
|
||||
|
||||
val targetLine = lines.firstOrNull { it.startsWith("--- a/") }
|
||||
?: throw Error("Patch $patchFile is missing a '--- a/...' header")
|
||||
val fullClassName = targetLine
|
||||
.removePrefix("--- a/")
|
||||
.removeSuffix(".smali")
|
||||
.trim()
|
||||
val patch = LoadedPatch(
|
||||
fullClassName = fullClassName,
|
||||
patch = UnifiedDiffUtils.parseUnifiedDiff(lines),
|
||||
)
|
||||
patches.add(patch)
|
||||
container.log("Loaded patch file $patchFile for class ${patch.fullClassName}")
|
||||
// Split into per-target sections — a single .patch may contain multiple
|
||||
// `--- a/...` blocks targeting different classes.
|
||||
val sections = splitMultiTargetPatch(lines)
|
||||
if (sections.isEmpty()) {
|
||||
throw Error("Patch $patchFile is missing a '--- a/...' header")
|
||||
}
|
||||
for (section in sections) {
|
||||
val targetLine = section.first { it.startsWith("--- a/") }
|
||||
val fullClassName = targetLine
|
||||
.removePrefix("--- a/")
|
||||
.removeSuffix(".smali")
|
||||
.trim()
|
||||
val patch = LoadedPatch(
|
||||
fullClassName = fullClassName,
|
||||
patch = UnifiedDiffUtils.parseUnifiedDiff(section),
|
||||
)
|
||||
patches.add(patch)
|
||||
container.log("Loaded patch file $patchFile for class ${patch.fullClassName}")
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
throw Error("Failed to parse patch file $patchFile", t)
|
||||
}
|
||||
@@ -313,6 +393,29 @@ class SmaliPatchStep(
|
||||
|
||||
private companion object {
|
||||
val LOCALS_DIRECTIVE = Regex("""^#\s*rl-locals:\s+(\S+)\s+(\S+)\s+(\d+)\s*$""")
|
||||
val UNRESOLVED_TOKEN = Regex("""__RL_[A-Z0-9_]+__""")
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a unified diff into per-target sections. A single `.patch` file may bundle
|
||||
* multiple file diffs (each starting with `--- a/...`); each section becomes its own
|
||||
* patch with its own target class. The header lines before the first `--- a/` (and
|
||||
* any `# rl-locals:` directives) are preserved by being copied into every section so
|
||||
* that `UnifiedDiffUtils.parseUnifiedDiff` can still parse the section in isolation.
|
||||
*/
|
||||
private fun splitMultiTargetPatch(lines: List<String>): List<List<String>> {
|
||||
val headerEnd = lines.indexOfFirst { it.startsWith("--- a/") }
|
||||
if (headerEnd < 0) return emptyList()
|
||||
val header = lines.subList(0, headerEnd)
|
||||
|
||||
val sectionStarts = lines.withIndex()
|
||||
.filter { (_, line) -> line.startsWith("--- a/") }
|
||||
.map { it.index }
|
||||
|
||||
return sectionStarts.mapIndexed { i, start ->
|
||||
val end = sectionStarts.getOrNull(i + 1) ?: lines.size
|
||||
header + lines.subList(start, end)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+45
-2
@@ -1,19 +1,30 @@
|
||||
package com.meowarex.rlmobile.patcher.steps.prepare
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.manager.CachedReleaseInfo
|
||||
import com.meowarex.rlmobile.manager.PathManager
|
||||
import com.meowarex.rlmobile.manager.ReleaseInfoCache
|
||||
import com.meowarex.rlmobile.network.models.RLBuildInfo
|
||||
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
|
||||
import com.meowarex.rlmobile.network.utils.getOrThrow
|
||||
import com.meowarex.rlmobile.patcher.StepRunner
|
||||
import com.meowarex.rlmobile.patcher.steps.StepGroup
|
||||
import com.meowarex.rlmobile.patcher.steps.base.Step
|
||||
import com.meowarex.rlmobile.util.isOnline
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@Stable
|
||||
class FetchInfoStep : Step(), KoinComponent {
|
||||
private val github: RadiantLyricsGithubService by inject()
|
||||
private val context: Application by inject()
|
||||
private val paths: PathManager by inject()
|
||||
private val json: Json by inject()
|
||||
|
||||
override val group = StepGroup.Prepare
|
||||
override val localizedName = R.string.patch_step_fetch_info
|
||||
@@ -25,6 +36,33 @@ class FetchInfoStep : Step(), KoinComponent {
|
||||
private set
|
||||
|
||||
override suspend fun execute(container: StepRunner) {
|
||||
// Fetch the latest release and refresh the cache (when online)
|
||||
if (context.isOnline()) {
|
||||
try {
|
||||
fetchFromRemote(container)
|
||||
return
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (t: Throwable) {
|
||||
container.log("Failed to fetch latest release: ${Log.getStackTraceString(t)}")
|
||||
container.log("Falling back to cached release info for a local re-patch")
|
||||
}
|
||||
} else {
|
||||
container.log("No network connection — using cached release info for a local re-patch")
|
||||
}
|
||||
|
||||
val cached = ReleaseInfoCache.load(paths, json)
|
||||
?: throw IllegalStateException(
|
||||
"Could not fetch the latest release and no cached release info is available. " +
|
||||
"Connect to the internet and try again."
|
||||
)
|
||||
data = cached.data
|
||||
patchesAssetUrl = cached.patchesAssetUrl
|
||||
container.log("Using cached build info: $data")
|
||||
container.log("Cached patches asset URL: $patchesAssetUrl")
|
||||
}
|
||||
|
||||
private suspend fun fetchFromRemote(container: StepRunner) {
|
||||
container.log("Fetching latest release from ${RadiantLyricsGithubService.REPO_OWNER}/${RadiantLyricsGithubService.REPO_NAME}")
|
||||
val release = github.getLatestRelease(force = true).getOrThrow()
|
||||
|
||||
@@ -33,14 +71,19 @@ class FetchInfoStep : Step(), KoinComponent {
|
||||
?.browserDownloadUrl
|
||||
?: throw IllegalStateException("No ${RadiantLyricsGithubService.DATA_JSON_ASSET_NAME} asset found in latest release ${release.tagName}")
|
||||
|
||||
patchesAssetUrl = release.assets
|
||||
val patchesUrl = release.assets
|
||||
.find { it.name == RadiantLyricsGithubService.PATCHES_ASSET_NAME }
|
||||
?.browserDownloadUrl
|
||||
?: throw IllegalStateException("No ${RadiantLyricsGithubService.PATCHES_ASSET_NAME} asset found in latest release ${release.tagName}")
|
||||
|
||||
container.log("Fetching build info from $dataJsonUrl")
|
||||
data = github.getBuildInfo(dataJsonUrl, force = true).getOrThrow()
|
||||
val buildInfo = github.getBuildInfo(dataJsonUrl, force = true).getOrThrow()
|
||||
|
||||
data = buildInfo
|
||||
patchesAssetUrl = patchesUrl
|
||||
container.log("Fetched build info: $data")
|
||||
container.log("Patches asset URL: $patchesAssetUrl")
|
||||
|
||||
ReleaseInfoCache.save(paths, json, CachedReleaseInfo(buildInfo, patchesUrl))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,16 @@ object ManifestPatcher {
|
||||
private const val IS_SPLIT_REQUIRED = "isSplitRequired"
|
||||
private const val REQUIRED_SPLIT_TYPES = "requiredSplitTypes"
|
||||
private const val SPLIT_TYPES = "splitTypes"
|
||||
private const val WAZE_PACKAGE = "com.waze"
|
||||
private const val WAZE_INIT_RECEIVER = "radiant.WazeInitReceiver"
|
||||
private const val WAZE_ACTION_INIT = "com.waze.sdk.audio.ACTION_INIT"
|
||||
|
||||
fun patchManifest(
|
||||
manifestBytes: ByteArray,
|
||||
packageName: String,
|
||||
appName: String,
|
||||
debuggable: Boolean,
|
||||
enableWazeIntegration: Boolean,
|
||||
): ByteArray {
|
||||
// Extract original package name to rewrite every reference to it
|
||||
// (permissions, provider authorities) to the new packageName.
|
||||
@@ -84,6 +88,49 @@ object ManifestPatcher {
|
||||
}
|
||||
|
||||
return when (name) {
|
||||
"queries" -> object : NodeVisitor(nv) {
|
||||
private var hasWazePackage = false
|
||||
|
||||
override fun child(ns: String?, name: String): NodeVisitor {
|
||||
val visitor = super.child(ns, name)
|
||||
|
||||
return if (enableWazeIntegration && name == "package") {
|
||||
object : NodeVisitor(visitor) {
|
||||
override fun attr(
|
||||
ns: String?,
|
||||
name: String?,
|
||||
resourceId: Int,
|
||||
type: Int,
|
||||
value: Any?,
|
||||
) {
|
||||
if (name == "name" && value == WAZE_PACKAGE) {
|
||||
hasWazePackage = true
|
||||
}
|
||||
super.attr(ns, name, resourceId, type, value)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
visitor
|
||||
}
|
||||
}
|
||||
|
||||
override fun end() {
|
||||
if (enableWazeIntegration && !hasWazePackage) {
|
||||
super.child(null, "package").apply {
|
||||
attr(
|
||||
ANDROID_NAMESPACE,
|
||||
"name",
|
||||
android.R.attr.name,
|
||||
TYPE_STRING,
|
||||
WAZE_PACKAGE,
|
||||
)
|
||||
end()
|
||||
}
|
||||
}
|
||||
super.end()
|
||||
}
|
||||
}
|
||||
|
||||
"uses-permission" -> object : NodeVisitor(nv) {
|
||||
override fun attr(ns: String?, name: String?, resourceId: Int, type: Int, value: Any?) {
|
||||
if (name != "maxSdkVersion") {
|
||||
@@ -132,6 +179,7 @@ object ManifestPatcher {
|
||||
private var addUseEmbeddedDex = true
|
||||
private var addExtractNativeLibs = true
|
||||
private var addMetadata = true
|
||||
private var hasWazeInitReceiver = false
|
||||
|
||||
override fun attr(ns: String?, name: String, resourceId: Int, type: Int, value: Any?) {
|
||||
if (name == REQUEST_LEGACY_EXTERNAL_STORAGE) addLegacyStorage = false
|
||||
@@ -161,6 +209,22 @@ object ManifestPatcher {
|
||||
|
||||
return when (name) {
|
||||
"activity" -> ReplaceAttrsVisitor(visitor, mapOf("label" to appName))
|
||||
|
||||
"receiver" -> object : NodeVisitor(visitor) {
|
||||
override fun attr(
|
||||
ns: String?,
|
||||
name: String,
|
||||
resourceId: Int,
|
||||
type: Int,
|
||||
value: Any?
|
||||
) {
|
||||
if (name == "name" && value == WAZE_INIT_RECEIVER) {
|
||||
hasWazeInitReceiver = true
|
||||
}
|
||||
super.attr(ns, name, resourceId, type, value)
|
||||
}
|
||||
}
|
||||
|
||||
"provider" -> object : NodeVisitor(visitor) {
|
||||
override fun attr(
|
||||
ns: String?,
|
||||
@@ -219,6 +283,39 @@ object ManifestPatcher {
|
||||
0
|
||||
)
|
||||
|
||||
if (enableWazeIntegration && !hasWazeInitReceiver) {
|
||||
super.child(null, "receiver").apply {
|
||||
attr(
|
||||
ANDROID_NAMESPACE,
|
||||
"name",
|
||||
android.R.attr.name,
|
||||
TYPE_STRING,
|
||||
WAZE_INIT_RECEIVER,
|
||||
)
|
||||
attr(
|
||||
ANDROID_NAMESPACE,
|
||||
"exported",
|
||||
android.R.attr.exported,
|
||||
TYPE_INT_BOOLEAN,
|
||||
1,
|
||||
)
|
||||
child(null, "intent-filter").apply {
|
||||
child(null, "action").apply {
|
||||
attr(
|
||||
ANDROID_NAMESPACE,
|
||||
"name",
|
||||
android.R.attr.name,
|
||||
TYPE_STRING,
|
||||
WAZE_ACTION_INIT,
|
||||
)
|
||||
end()
|
||||
}
|
||||
end()
|
||||
}
|
||||
end()
|
||||
}
|
||||
}
|
||||
|
||||
super.end()
|
||||
}
|
||||
}
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.previews.dialogs
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.meowarex.rlmobile.ui.screens.logs.components.dialogs.DeleteLogsDialog
|
||||
import com.meowarex.rlmobile.ui.theme.ManagerTheme
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun DeleteLogsDialogPreview() {
|
||||
ManagerTheme {
|
||||
DeleteLogsDialog(
|
||||
onConfirm = {},
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
+16
-1
@@ -2,11 +2,14 @@ package com.meowarex.rlmobile.ui.previews.screens
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.*
|
||||
import com.meowarex.rlmobile.network.utils.SemVer
|
||||
import com.meowarex.rlmobile.ui.screens.componentopts.PatchComponent
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.*
|
||||
import com.meowarex.rlmobile.ui.theme.ManagerTheme
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.time.Clock
|
||||
|
||||
// This preview has scrollable/interactable content that cannot be tested from an IDE preview
|
||||
@@ -18,25 +21,37 @@ private fun PatchOptionsScreenPreview(
|
||||
@PreviewParameter(PatchOptionsParametersProvider::class)
|
||||
parameters: PatchOptionsParameters,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val specs = remember { builtinPatchSpecs(context, Json { ignoreUnknownKeys = true }) }
|
||||
|
||||
ManagerTheme {
|
||||
PatchOptionsScreenContent(
|
||||
isUpdate = parameters.isUpdate,
|
||||
isDevMode = parameters.isDevMode,
|
||||
debuggable = parameters.debuggable,
|
||||
setDebuggable = {},
|
||||
bypassIncompatible = false,
|
||||
setBypassIncompatible = {},
|
||||
appName = parameters.appName,
|
||||
appNameIsError = parameters.appNameIsError,
|
||||
setAppName = {},
|
||||
packageName = parameters.packageName,
|
||||
packageNameState = parameters.packageNameState,
|
||||
setPackageName = {},
|
||||
packageNameLocked = parameters.packageName == PatchOptions.Default.packageName,
|
||||
onUnlockPackageName = {},
|
||||
onLockPackageName = {},
|
||||
customTidalApk = parameters.customTidalApk,
|
||||
onSelectCustomTidalApk = {},
|
||||
customPatches = parameters.customPatches,
|
||||
onSelectCustomPatches = {},
|
||||
enabledPatchCount = KnownPatch.All.size,
|
||||
specs = specs,
|
||||
isPatchEnabled = { true },
|
||||
onTogglePatch = { _, _ -> },
|
||||
patchLockState = { PatchLock.Free },
|
||||
variantIndex = { 0 },
|
||||
onSelectVariant = { _, _ -> },
|
||||
optionState = PatchOptionState.Preview,
|
||||
isConfigValid = parameters.isConfigValid,
|
||||
onInstall = {},
|
||||
)
|
||||
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.previews.screens.logs
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogEntry
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogsScreenContent
|
||||
import com.meowarex.rlmobile.ui.theme.ManagerTheme
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.util.UUID
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun LogsListScreenNonePreview() {
|
||||
ManagerTheme {
|
||||
LogsScreenContent(
|
||||
logs = logs,
|
||||
onOpenLog = {},
|
||||
onDeleteLogs = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val logs: SnapshotStateList<LogEntry> = mutableStateListOf(
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = false,
|
||||
installDate = "5 min. ago, 10:18 AM",
|
||||
durationSecs = 18.555f,
|
||||
stacktracePreview = null,
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = true,
|
||||
installDate = "7 min. ago, 10:17 AM",
|
||||
durationSecs = 73.095f,
|
||||
stacktracePreview = persistentListOf(
|
||||
"kotlinx.coroutines.JobCancellationException: Job was cancelled; job=SupervisorJobImpl{Cancelling}@833e76f]",
|
||||
),
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = false,
|
||||
installDate = "Yesterday, 11:37 PM",
|
||||
durationSecs = 58.439f,
|
||||
stacktracePreview = null,
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = true,
|
||||
installDate = "Yesterday, 11:17 PM",
|
||||
durationSecs = 24.405f,
|
||||
stacktracePreview = persistentListOf(
|
||||
"java.lang.Error: Installation was aborted or cancelled",
|
||||
),
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = true,
|
||||
installDate = "Yesterday, 11:17 PM",
|
||||
durationSecs = 0.057f,
|
||||
stacktracePreview = persistentListOf(
|
||||
"java.lang.IllegalStateException: balls",
|
||||
"\tat com.meowarex.rlmobile.patcher.steps.prepare.FetchInfoStep.execute(FetchInfoStep.kt:31)",
|
||||
"\tat com.meowarex.rlmobile.patcher.steps.prepare.FetchInfoStep\$execute\\$1.invokeSuspend(Unknown Source:15)",
|
||||
),
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = false,
|
||||
installDate = "Yesterday, 1:11 PM",
|
||||
durationSecs = 210.539f,
|
||||
stacktracePreview = null,
|
||||
),
|
||||
)
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.previews.screens.logs
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogsScreenContent
|
||||
import com.meowarex.rlmobile.ui.theme.ManagerTheme
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun LogsListScreenNonePreview() {
|
||||
ManagerTheme {
|
||||
LogsScreenContent(
|
||||
logs = remember { mutableStateListOf() },
|
||||
onOpenLog = {},
|
||||
onDeleteLogs = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.github.diamondminer88.zip.ZipReader
|
||||
import com.meowarex.rlmobile.BuildConfig
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.manager.PathManager
|
||||
import com.meowarex.rlmobile.manager.PreferencesManager
|
||||
import com.meowarex.rlmobile.manager.ReleaseInfoCache
|
||||
import com.meowarex.rlmobile.network.models.GithubCommit
|
||||
import com.meowarex.rlmobile.network.models.RLBuildInfo
|
||||
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
|
||||
@@ -42,6 +44,7 @@ class HomeModel(
|
||||
private val github: RadiantLyricsGithubService,
|
||||
private val json: Json,
|
||||
private val prefs: PreferencesManager,
|
||||
private val paths: PathManager,
|
||||
) : ScreenModel {
|
||||
|
||||
var state by mutableStateOf<HomeState>(HomeState.Loading)
|
||||
@@ -117,18 +120,22 @@ class HomeModel(
|
||||
|
||||
val remote = remoteDataJson
|
||||
val currentPatches = remote?.patchesVersion?.toString()
|
||||
val installedPatches = installMetadata?.patchesVersion?.toString()
|
||||
val previousPatches = initialPrefPatchesVersion.ifEmpty {
|
||||
installMetadata?.patchesVersion?.toString().orEmpty()
|
||||
installedPatches.orEmpty()
|
||||
}
|
||||
val patchesFrom = previousPatches.takeIf { it.isNotEmpty() }
|
||||
val patchesTo = currentPatches ?: previousPatches.ifEmpty { "?" }
|
||||
val patchesUpdateAvailable = installedPatches != null
|
||||
&& currentPatches != null
|
||||
&& installedPatches != currentPatches
|
||||
add(
|
||||
VersionDelta(
|
||||
label = application.getString(R.string.manager_update_row_patches),
|
||||
iconRes = R.drawable.ic_extension,
|
||||
from = patchesFrom,
|
||||
to = patchesTo,
|
||||
tag = if (patchesFrom != null && patchesFrom != patchesTo)
|
||||
tag = if (patchesUpdateAvailable)
|
||||
application.getString(R.string.manager_update_tag_available) else null,
|
||||
)
|
||||
)
|
||||
@@ -142,13 +149,16 @@ class HomeModel(
|
||||
val tidalTo = currentTidal?.toString()
|
||||
?: previousTidal.takeIf { it > 0 }?.toString()
|
||||
?: "?"
|
||||
val tidalUpdateAvailable = installedTidalVersionCode > 0
|
||||
&& currentTidal != null
|
||||
&& installedTidalVersionCode != currentTidal
|
||||
add(
|
||||
VersionDelta(
|
||||
label = application.getString(R.string.manager_update_row_tidal),
|
||||
iconRes = R.drawable.ic_music_note,
|
||||
from = tidalFrom,
|
||||
to = tidalTo,
|
||||
tag = if (tidalFrom != null && tidalFrom != tidalTo)
|
||||
tag = if (tidalUpdateAvailable)
|
||||
application.getString(R.string.manager_update_tag_available) else null,
|
||||
)
|
||||
)
|
||||
@@ -163,16 +173,23 @@ class HomeModel(
|
||||
|
||||
refreshingLock.withLock {
|
||||
val pkg = fetchInstalled()
|
||||
val remote = async(Dispatchers.IO) { if (remoteDataJson == null) fetchRemoteData() }
|
||||
remote.await()
|
||||
// Skip the remote fetch entirely when offline
|
||||
val online = application.isOnline()
|
||||
if (online) {
|
||||
val remote = async(Dispatchers.IO) { if (remoteDataJson == null) fetchRemoteData() }
|
||||
remote.await()
|
||||
}
|
||||
|
||||
val install = pkg?.toInstallData()
|
||||
val latest = remoteDataJson?.tidalVersionCode
|
||||
val offlineRepatchReady = hasOfflineRepatchAssets()
|
||||
|
||||
mainThread {
|
||||
state = HomeState.Loaded(
|
||||
install = install,
|
||||
latestTidalVersionCode = latest,
|
||||
offline = !online,
|
||||
offlineRepatchReady = offlineRepatchReady,
|
||||
)
|
||||
maybeCheckManagerUpdate(pkg)
|
||||
}
|
||||
@@ -250,6 +267,13 @@ class HomeModel(
|
||||
)
|
||||
}
|
||||
|
||||
// check if an offline "Local Repatch" is possible
|
||||
private fun hasOfflineRepatchAssets(): Boolean {
|
||||
val info = ReleaseInfoCache.load(paths, json) ?: return false
|
||||
return paths.hasCachedTidalApk(info.data.tidalVersionCode) &&
|
||||
paths.hasCachedSmaliPatches(info.data.patchesVersion)
|
||||
}
|
||||
|
||||
private suspend fun fetchRemoteData() {
|
||||
val release = try {
|
||||
github.getLatestRelease().fold(
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -32,13 +33,14 @@ import com.meowarex.rlmobile.ui.components.SegmentedButton
|
||||
import com.meowarex.rlmobile.ui.components.Tag
|
||||
import com.meowarex.rlmobile.ui.screens.about.AboutScreen
|
||||
import com.meowarex.rlmobile.ui.screens.home.components.CommitList
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogsListScreen
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionsScreen
|
||||
import com.meowarex.rlmobile.ui.screens.settings.SettingsScreen
|
||||
import com.meowarex.rlmobile.ui.widgets.managerupdate.ManagerUpdateDialog
|
||||
import com.meowarex.rlmobile.ui.widgets.updater.UpdaterViewModel
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
@Parcelize
|
||||
class HomeScreen : Screen, Parcelable {
|
||||
@@ -50,6 +52,9 @@ class HomeScreen : Screen, Parcelable {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val scope = rememberCoroutineScope()
|
||||
val model = koinScreenModel<HomeModel>()
|
||||
val activity = LocalContext.current as ComponentActivity
|
||||
val updater = koinViewModel<UpdaterViewModel>(viewModelStoreOwner = activity)
|
||||
val managerUpdateAvailable = updater.targetVersion != null
|
||||
|
||||
LifecycleResumeEffect(Unit) {
|
||||
model.refresh(delay = true)
|
||||
@@ -67,18 +72,20 @@ class HomeScreen : Screen, Parcelable {
|
||||
contentDescription = stringResource(R.string.navigation_refresh),
|
||||
)
|
||||
}
|
||||
if (managerUpdateAvailable) {
|
||||
IconButton(onClick = updater::reopenDialog) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_update),
|
||||
contentDescription = stringResource(R.string.action_update),
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { navigator.push(AboutScreen()) }) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_info),
|
||||
contentDescription = stringResource(R.string.navigation_about),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { navigator.push(LogsListScreen()) }) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_receipt),
|
||||
contentDescription = stringResource(R.string.navigation_logs),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { navigator.push(SettingsScreen()) }) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_settings),
|
||||
@@ -107,6 +114,7 @@ class HomeScreen : Screen, Parcelable {
|
||||
is HomeState.Loaded -> HomeContent(
|
||||
state = state,
|
||||
commits = model.commits,
|
||||
managerUpdateAvailable = managerUpdateAvailable,
|
||||
onInstall = { navigator.pushOnce(PatchOptionsScreen()) },
|
||||
onRepatch = {
|
||||
scope.launchIO {
|
||||
@@ -134,6 +142,7 @@ class HomeScreen : Screen, Parcelable {
|
||||
private fun ColumnScope.HomeContent(
|
||||
state: HomeState.Loaded,
|
||||
commits: kotlinx.coroutines.flow.Flow<androidx.paging.PagingData<com.meowarex.rlmobile.network.models.GithubCommit>>,
|
||||
managerUpdateAvailable: Boolean,
|
||||
onInstall: () -> Unit,
|
||||
onRepatch: () -> Unit,
|
||||
onLaunch: () -> Unit,
|
||||
@@ -200,15 +209,24 @@ private fun ColumnScope.HomeContent(
|
||||
}
|
||||
}
|
||||
|
||||
val blockedByManagerUpdate = managerUpdateAvailable && (patchesBehind || tidalBehind)
|
||||
val canLocalRepatch = install != null && state.offlineRepatchReady
|
||||
val onlineEnabled = state.latestTidalVersionCode != null || install != null
|
||||
val buttonEnabled = !blockedByManagerUpdate && (if (state.offline) canLocalRepatch else onlineEnabled)
|
||||
Button(
|
||||
onClick = if (install == null) onInstall else onRepatch,
|
||||
enabled = state.latestTidalVersionCode != null,
|
||||
enabled = buttonEnabled,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
val label = when {
|
||||
state.latestTidalVersionCode == null -> "Loading…"
|
||||
blockedByManagerUpdate -> "Manager Update Required"
|
||||
state.offline && install != null -> "Local Repatch"
|
||||
state.offline -> "No Network"
|
||||
install == null && state.latestTidalVersionCode == null -> "Loading…"
|
||||
install == null -> "Install"
|
||||
install.isUpToDate == false -> "Update"
|
||||
patchesBehind && tidalBehind -> "Update Patches & TIDAL"
|
||||
patchesBehind -> "Update Patches"
|
||||
tidalBehind -> "Update TIDAL"
|
||||
else -> "Repatch"
|
||||
}
|
||||
Text(
|
||||
@@ -221,6 +239,18 @@ private fun ColumnScope.HomeContent(
|
||||
)
|
||||
}
|
||||
|
||||
if (state.offline && install != null) {
|
||||
Text(
|
||||
text = "No Network Connection",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 6.dp),
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = install != null) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
|
||||
@@ -8,5 +8,7 @@ sealed interface HomeState {
|
||||
data class Loaded(
|
||||
val install: InstallData?,
|
||||
val latestTidalVersionCode: Int?,
|
||||
val offline: Boolean = false,
|
||||
val offlineRepatchReady: Boolean = false,
|
||||
) : HomeState
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.core.screen.ScreenKey
|
||||
import cafe.adriel.voyager.koin.koinScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.meowarex.rlmobile.ui.screens.log.LogScreen
|
||||
import com.meowarex.rlmobile.ui.screens.logs.components.*
|
||||
import com.meowarex.rlmobile.ui.screens.logs.components.dialogs.DeleteLogsDialog
|
||||
import com.meowarex.rlmobile.ui.util.paddings.PaddingValuesSides
|
||||
import com.meowarex.rlmobile.ui.util.paddings.exclude
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class LogsListScreen : Screen, Parcelable {
|
||||
@IgnoredOnParcel
|
||||
override val key: ScreenKey
|
||||
get() = "LogsScreen"
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val model = koinScreenModel<LogsListScreenModel>()
|
||||
|
||||
var showWipeConfirmDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showWipeConfirmDialog) {
|
||||
DeleteLogsDialog(
|
||||
onConfirm = {
|
||||
showWipeConfirmDialog = false
|
||||
model.deleteLogs()
|
||||
},
|
||||
onDismiss = {
|
||||
showWipeConfirmDialog = false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
LogsScreenContent(
|
||||
logs = model.logEntries,
|
||||
onOpenLog = { navigator.push(LogScreen(installId = it)) },
|
||||
onDeleteLogs = { showWipeConfirmDialog = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LogsScreenContent(
|
||||
logs: SnapshotStateList<LogEntry>,
|
||||
onOpenLog: (id: String) -> Unit,
|
||||
onDeleteLogs: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
LogsListAppBar(
|
||||
onDeleteLogs = onDeleteLogs,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = paddingValues.exclude(PaddingValuesSides.Horizontal + PaddingValuesSides.Top),
|
||||
modifier = Modifier
|
||||
.padding(paddingValues.exclude(PaddingValuesSides.Bottom))
|
||||
.padding(vertical = 12.dp, horizontal = 22.dp)
|
||||
) {
|
||||
if (logs.isEmpty()) {
|
||||
item(key = "EMPTY") {
|
||||
LogsNone(
|
||||
modifier = Modifier.fillParentMaxSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(
|
||||
items = logs,
|
||||
contentType = { "LOG" },
|
||||
key = { it.id },
|
||||
) { data ->
|
||||
LogEntryCard(
|
||||
data = data,
|
||||
onClick = remember(onOpenLog, data.id) { { onOpenLog(data.id) } },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs
|
||||
|
||||
import android.app.Application
|
||||
import android.text.format.DateUtils
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.manager.InstallLogManager
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
class LogsListScreenModel(
|
||||
private val logsManager: InstallLogManager,
|
||||
private val application: Application,
|
||||
) : ScreenModel {
|
||||
/**
|
||||
* All the loaded log entries sorted descending by creation date.
|
||||
*/
|
||||
val logEntries = mutableStateListOf<LogEntry>()
|
||||
|
||||
init {
|
||||
loadLogsList()
|
||||
}
|
||||
|
||||
fun deleteLogs() = screenModelScope.launchIO {
|
||||
logsManager.deleteAllEntries()
|
||||
|
||||
mainThread {
|
||||
logEntries.clear()
|
||||
application.showToast(R.string.logs_status_delete_success)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadLogsList() = screenModelScope.launchIO {
|
||||
for (installId in logsManager.fetchInstallDataEntries()) {
|
||||
val data = logsManager.fetchInstallData(id = installId)
|
||||
?: continue
|
||||
|
||||
val entry = LogEntry(
|
||||
id = data.id,
|
||||
isError = data.isError,
|
||||
installDate = DateUtils.getRelativeDateTimeString(
|
||||
/* c = */ application,
|
||||
/* time = */ data.installDate.toEpochMilliseconds(),
|
||||
/* minResolution = */ DateUtils.SECOND_IN_MILLIS,
|
||||
/* transitionResolution = */ DateUtils.WEEK_IN_MILLIS,
|
||||
/* flags = */ DateUtils.FORMAT_ABBREV_ALL,
|
||||
).toString(),
|
||||
durationSecs = data.installDuration.inWholeMilliseconds / 1000f,
|
||||
stacktracePreview = data.errorStacktrace
|
||||
?.splitToSequence('\n')
|
||||
?.take(3)
|
||||
?.toImmutableList(),
|
||||
)
|
||||
|
||||
mainThread { logEntries += entry }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class LogEntry(
|
||||
val id: String,
|
||||
val isError: Boolean,
|
||||
val installDate: String,
|
||||
val durationSecs: Float,
|
||||
val stacktracePreview: ImmutableList<String>?,
|
||||
)
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.*
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.patcher.steps.base.StepState
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogEntry
|
||||
import com.meowarex.rlmobile.ui.screens.patching.components.StepStateIcon
|
||||
import com.meowarex.rlmobile.ui.screens.patching.components.TimeElapsed
|
||||
|
||||
@Composable
|
||||
fun LogEntryCard(
|
||||
data: LogEntry,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val errorColor = MaterialTheme.colorScheme.error
|
||||
|
||||
ElevatedCard(
|
||||
shape = RectangleShape,
|
||||
modifier = modifier
|
||||
.clickable(onClick = onClick)
|
||||
.clip(RoundedCornerShape(topStart = 6.dp, 12.0.dp, bottomStart = 6.dp, bottomEnd = 12.0.dp))
|
||||
.drawWithCache {
|
||||
val color = when (data.isError) {
|
||||
true -> errorColor
|
||||
false -> Color(0xFF59B463)
|
||||
}
|
||||
|
||||
onDrawWithContent {
|
||||
drawContent()
|
||||
drawRect(
|
||||
color = color,
|
||||
alpha = .8f,
|
||||
topLeft = Offset.Zero,
|
||||
size = Size(4.dp.toPx(), size.height),
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 18.dp, horizontal = 22.dp),
|
||||
) {
|
||||
StepStateIcon(
|
||||
state = if (data.isError) StepState.Error else StepState.Success,
|
||||
size = 24.dp,
|
||||
)
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = when (data.isError) {
|
||||
true -> stringResource(R.string.status_failed)
|
||||
false -> stringResource(R.string.status_success)
|
||||
},
|
||||
)
|
||||
|
||||
Text(
|
||||
text = data.installDate,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
modifier = Modifier.alpha(.6f),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.weight(1f, fill = true))
|
||||
|
||||
TimeElapsed(
|
||||
seconds = data.durationSecs,
|
||||
modifier = Modifier.alpha(.9f),
|
||||
)
|
||||
}
|
||||
|
||||
if (data.stacktracePreview != null) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 26.dp, end = 20.dp, bottom = 18.dp)
|
||||
// https://stackoverflow.com/a/76270310/13964629
|
||||
.graphicsLayer(
|
||||
alpha = .95f,
|
||||
compositingStrategy = CompositingStrategy.Offscreen,
|
||||
)
|
||||
.drawWithContent {
|
||||
val colors = listOf(Color.Black, Color.Black, Color.Transparent)
|
||||
drawContent()
|
||||
drawRect(
|
||||
brush = Brush.verticalGradient(colors),
|
||||
blendMode = BlendMode.DstIn,
|
||||
)
|
||||
}
|
||||
) {
|
||||
// The stacktrace is separated into multiple Text elements because ellipsis is not supported per-line
|
||||
for (line in data.stacktracePreview) key(line) {
|
||||
Text(
|
||||
text = line,
|
||||
softWrap = false,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
lineHeight = 18.sp,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
fontFamily = FontFamily.Companion.Monospace,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs.components
|
||||
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.components.BackButton
|
||||
import com.meowarex.rlmobile.ui.screens.settings.SettingsScreen
|
||||
|
||||
@Composable
|
||||
fun LogsListAppBar(
|
||||
onDeleteLogs: () -> Unit,
|
||||
) {
|
||||
TopAppBar(
|
||||
navigationIcon = { BackButton() },
|
||||
title = { Text(stringResource(R.string.logs_title)) },
|
||||
actions = {
|
||||
val navigator = LocalNavigator.current
|
||||
|
||||
IconButton(onClick = onDeleteLogs) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_delete_forever),
|
||||
contentDescription = stringResource(R.string.logs_action_delete_all)
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { navigator?.push(SettingsScreen()) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_settings),
|
||||
contentDescription = stringResource(R.string.navigation_settings)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
|
||||
@Composable
|
||||
fun LogsNone(modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier) {
|
||||
Column(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_reciept_off),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.logs_none),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.alpha(.8f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs.components.dialogs
|
||||
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
|
||||
@Composable
|
||||
fun DeleteLogsDialog(
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
icon = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_delete_forever),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(32.dp),
|
||||
)
|
||||
},
|
||||
title = { Text(stringResource(R.string.logs_wipe_title)) },
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.logs_wipe_desc),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = onConfirm,
|
||||
) {
|
||||
Text(stringResource(R.string.action_confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(
|
||||
onClick = onDismiss,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
) {
|
||||
Text(stringResource(R.string.action_cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.meowarex.rlmobile.R
|
||||
|
||||
enum class KnownPatch(
|
||||
val fileNames: List<String>,
|
||||
@StringRes val titleRes: Int,
|
||||
@StringRes val descRes: Int,
|
||||
val requires: List<KnownPatch> = emptyList(),
|
||||
val disables: List<KnownPatch> = emptyList(),
|
||||
) {
|
||||
// Dependency-first order (later refs need backward resolution)
|
||||
LyricsDisableCover(
|
||||
fileNames = listOf("lyrics-disable-cover.patch"),
|
||||
titleRes = R.string.patch_lyrics_disable_cover_title,
|
||||
descRes = R.string.patch_lyrics_disable_cover_desc,
|
||||
),
|
||||
LyricsReplaceLyricsButton(
|
||||
fileNames = listOf(
|
||||
"lyrics-replace-lyrics-button.patch",
|
||||
"lyrics-sparkle-conditional-visibility.patch",
|
||||
),
|
||||
titleRes = R.string.patch_lyrics_replace_button_title,
|
||||
descRes = R.string.patch_lyrics_replace_button_desc,
|
||||
),
|
||||
LyricsReplaceShareButton(
|
||||
fileNames = listOf("lyrics-replace-share-button.patch"),
|
||||
titleRes = R.string.patch_lyrics_replace_share_button_title,
|
||||
descRes = R.string.patch_lyrics_replace_share_button_desc,
|
||||
),
|
||||
PlayerBackdrop(
|
||||
fileNames = listOf("player-backdrop.patch"),
|
||||
titleRes = R.string.patch_player_backdrop_title,
|
||||
descRes = R.string.patch_player_backdrop_desc,
|
||||
),
|
||||
DebugMenuUnlock(
|
||||
fileNames = listOf("debug-menu-unlock.patch"),
|
||||
titleRes = R.string.patch_debug_menu_unlock_title,
|
||||
descRes = R.string.patch_debug_menu_unlock_desc,
|
||||
),
|
||||
LyricsProgressPill(
|
||||
fileNames = listOf(
|
||||
"lyrics-progress-pill.patch",
|
||||
"lyrics-fade-region.patch",
|
||||
),
|
||||
titleRes = R.string.patch_lyrics_progress_pill_title,
|
||||
descRes = R.string.patch_lyrics_progress_pill_desc,
|
||||
requires = listOf(LyricsDisableCover, LyricsReplaceShareButton),
|
||||
),
|
||||
EnableLegacyUi(
|
||||
fileNames = listOf("enable-legacy-ui.patch"),
|
||||
titleRes = R.string.patch_enable_legacy_ui_title,
|
||||
descRes = R.string.patch_enable_legacy_ui_desc,
|
||||
requires = listOf(DebugMenuUnlock),
|
||||
disables = listOf(
|
||||
LyricsDisableCover,
|
||||
LyricsReplaceLyricsButton,
|
||||
LyricsReplaceShareButton,
|
||||
PlayerBackdrop,
|
||||
LyricsProgressPill,
|
||||
),
|
||||
);
|
||||
|
||||
companion object {
|
||||
// Alphabetical by first filename, but pin DebugMenuUnlock to the bottom
|
||||
val All: List<KnownPatch> = entries.sortedWith(
|
||||
compareBy({ it == DebugMenuUnlock }, { it.fileNames.first() })
|
||||
)
|
||||
}
|
||||
}
|
||||
+117
-5
@@ -32,16 +32,128 @@ data class PatchOptions(
|
||||
*/
|
||||
val customPatches: PatchComponent? = null,
|
||||
|
||||
val disabledPatches: Set<String> = emptySet(),
|
||||
val patchStates: Map<String, Boolean> = emptyMap(),
|
||||
|
||||
val selectedVariants: Map<String, Int> = emptyMap(),
|
||||
val optionFloats: Map<String, Float> = emptyMap(),
|
||||
val optionBools: Map<String, Boolean> = emptyMap(),
|
||||
val optionInts: Map<String, Int> = emptyMap(),
|
||||
) : Parcelable {
|
||||
|
||||
fun isEnabled(spec: PatchSpec): Boolean =
|
||||
patchStates[spec.id] ?: spec.defaultEnabled
|
||||
|
||||
fun variantIndex(spec: PatchSpec): Int {
|
||||
val stored = (selectedVariants[spec.id] ?: spec.defaultVariantIndex)
|
||||
.coerceIn(0, spec.variants.lastIndex.coerceAtLeast(0))
|
||||
return spec.resolveVariantIndex(stored) { isToggleOn(spec, it) }
|
||||
}
|
||||
|
||||
fun sliderValue(spec: PatchSpec, option: OptionSpec.Slider): Float =
|
||||
(optionFloats["${spec.id}/${option.key}"] ?: option.default).coerceIn(option.min, option.max)
|
||||
|
||||
fun choiceIndex(spec: PatchSpec, option: OptionSpec.Choice): Int =
|
||||
(optionInts["${spec.id}/${option.key}"] ?: option.defaultIndex)
|
||||
.coerceIn(0, option.entries.lastIndex.coerceAtLeast(0))
|
||||
|
||||
fun colorValue(spec: PatchSpec, option: OptionSpec.Color): Int =
|
||||
optionInts["${spec.id}/${option.key}"] ?: option.default
|
||||
|
||||
fun isToggleOn(spec: PatchSpec, option: OptionSpec.Toggle): Boolean =
|
||||
optionBools["${spec.id}/${option.key}"] ?: option.default
|
||||
|
||||
fun isToggleActive(spec: PatchSpec, option: OptionSpec.Toggle): Boolean {
|
||||
if (!isToggleOn(spec, option)) return false
|
||||
option.requiresVariant?.let { if (variantIndex(spec) != it) return false }
|
||||
option.requiresOption?.let { key ->
|
||||
val required = spec.advancedOptions.filterIsInstance<OptionSpec.Toggle>()
|
||||
.firstOrNull { it.key == key }
|
||||
if (required != null && !isToggleOn(spec, required)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun disabledPatchFiles(specs: List<PatchSpec>): Set<String> = buildSet {
|
||||
for (spec in specs) {
|
||||
val enabled = isEnabled(spec)
|
||||
if (spec.variants.isEmpty()) {
|
||||
if (!enabled) addAll(spec.fileNames)
|
||||
} else {
|
||||
// base fileNames apply for every variant while enabled; disable them when off
|
||||
if (!enabled) addAll(spec.fileNames)
|
||||
val selected = variantIndex(spec)
|
||||
spec.variants.forEachIndexed { index, variant ->
|
||||
if (!enabled || index != selected) addAll(variant.fileNames)
|
||||
}
|
||||
}
|
||||
// Toggle sub-options that gate patch files.
|
||||
for (option in spec.advancedOptions) {
|
||||
if (option is OptionSpec.Toggle && option.fileNames.isNotEmpty()) {
|
||||
if (!enabled || !isToggleActive(spec, option)) addAll(option.fileNames)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun knownExtensionFiles(specs: List<PatchSpec>): Set<String> = buildSet {
|
||||
for (spec in specs) {
|
||||
addAll(spec.extensionFiles)
|
||||
spec.variants.forEach { addAll(it.extensionFiles) }
|
||||
spec.advancedOptions.forEach { if (it is OptionSpec.Toggle) addAll(it.extensionFiles) }
|
||||
}
|
||||
}
|
||||
|
||||
fun enabledExtensionFiles(specs: List<PatchSpec>): Set<String> = buildSet {
|
||||
for (spec in specs) {
|
||||
if (!isEnabled(spec)) continue
|
||||
addAll(spec.extensionFiles)
|
||||
if (spec.variants.isNotEmpty()) {
|
||||
spec.variants.getOrNull(variantIndex(spec))?.let { addAll(it.extensionFiles) }
|
||||
}
|
||||
for (option in spec.advancedOptions) {
|
||||
if (option is OptionSpec.Toggle && isToggleActive(spec, option)) addAll(option.extensionFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun smaliSubstitutions(specs: List<PatchSpec>): Map<String, String> = buildMap {
|
||||
for (spec in specs) {
|
||||
if (!isEnabled(spec)) continue
|
||||
for (option in spec.advancedOptions) {
|
||||
when (option) {
|
||||
is OptionSpec.Toggle -> {
|
||||
val token = option.token ?: continue
|
||||
put("__${token}__", if (isToggleActive(spec, option)) "0x1" else "0x0")
|
||||
}
|
||||
is OptionSpec.Slider -> {
|
||||
val token = option.token ?: continue
|
||||
val encode = option.encode ?: continue
|
||||
put("__${token}__", encode.encode(sliderValue(spec, option)))
|
||||
}
|
||||
is OptionSpec.Choice -> {
|
||||
val token = option.token ?: continue
|
||||
val gatedOff = option.requiresOption?.let { key ->
|
||||
spec.advancedOptions.filterIsInstance<OptionSpec.Toggle>()
|
||||
.firstOrNull { it.key == key }
|
||||
?.let { !isToggleOn(spec, it) }
|
||||
} ?: false
|
||||
val index = if (gatedOff) 0 else choiceIndex(spec, option)
|
||||
put("__${token}__", option.values.getOrNull(index) ?: continue)
|
||||
}
|
||||
is OptionSpec.Color -> {
|
||||
val token = option.token ?: continue
|
||||
put("__${token}__", colorValue(spec, option).toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Default = PatchOptions(
|
||||
val Default: PatchOptions = PatchOptions(
|
||||
appName = "TIDAL",
|
||||
packageName = "com.aspiro.tidal",
|
||||
debuggable = false,
|
||||
customTidalApk = null,
|
||||
customPatches = null,
|
||||
disabledPatches = (KnownPatch.DebugMenuUnlock.fileNames + KnownPatch.EnableLegacyUi.fileNames).toSet(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+295
-19
@@ -2,21 +2,35 @@ package com.meowarex.rlmobile.ui.screens.patchopts
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager.NameNotFoundException
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.*
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
import com.github.diamondminer88.zip.ZipReader
|
||||
import com.meowarex.rlmobile.BuildConfig
|
||||
import com.meowarex.rlmobile.manager.PathManager
|
||||
import com.meowarex.rlmobile.manager.PreferencesManager
|
||||
import com.meowarex.rlmobile.ui.screens.componentopts.ComponentOptionsScreen
|
||||
import com.meowarex.rlmobile.ui.screens.componentopts.PatchComponent
|
||||
import com.meowarex.rlmobile.ui.util.pushForResult
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import com.meowarex.rlmobile.manager.download.IDownloadManager
|
||||
import com.meowarex.rlmobile.manager.download.KtorDownloadManager
|
||||
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
|
||||
import com.meowarex.rlmobile.network.utils.getOrThrow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
|
||||
class PatchOptionsModel(
|
||||
prefilledOptions: PatchOptions,
|
||||
private val context: Context,
|
||||
private val prefs: PreferencesManager,
|
||||
private val paths: PathManager,
|
||||
private val json: Json,
|
||||
private val github: RadiantLyricsGithubService,
|
||||
private val downloader: KtorDownloadManager,
|
||||
) : ScreenModel {
|
||||
var packageName by mutableStateOf(prefilledOptions.packageName)
|
||||
private set
|
||||
@@ -29,6 +43,24 @@ class PatchOptionsModel(
|
||||
fetchPkgNameStateDebounced()
|
||||
}
|
||||
|
||||
/** Patches flagged [PatchSpec.pathLocked] only work under the stock package name. */
|
||||
fun isBlockedByPackageName(spec: PatchSpec): Boolean =
|
||||
spec.pathLocked && packageName != PatchOptions.Default.packageName && !bypassIncompatible
|
||||
|
||||
/** The package-name field starts locked & editing requires confirming the unlock dialog */
|
||||
var packageNameLocked by mutableStateOf(prefilledOptions.packageName == PatchOptions.Default.packageName)
|
||||
private set
|
||||
|
||||
fun unlockPackageName() {
|
||||
packageNameLocked = false
|
||||
}
|
||||
|
||||
/** Re-locking snaps the package name back to the stock default */
|
||||
fun lockPackageName() {
|
||||
packageNameLocked = true
|
||||
changePackageName(PatchOptions.Default.packageName)
|
||||
}
|
||||
|
||||
var appName by mutableStateOf(prefilledOptions.appName)
|
||||
private set
|
||||
|
||||
@@ -47,39 +79,171 @@ class PatchOptionsModel(
|
||||
debuggable = value
|
||||
}
|
||||
|
||||
var disabledPatches by mutableStateOf(prefilledOptions.disabledPatches)
|
||||
/** Dev override: force-allow path-gated ("incompatible") patches under a non-stock package name. */
|
||||
var bypassIncompatible by mutableStateOf(prefs.bypassIncompatible)
|
||||
private set
|
||||
|
||||
fun isPatchEnabled(patch: KnownPatch): Boolean =
|
||||
patch.fileNames.none { it in disabledPatches }
|
||||
fun changeBypassIncompatible(value: Boolean) {
|
||||
bypassIncompatible = value
|
||||
prefs.bypassIncompatible = value
|
||||
validatePatchSelection()
|
||||
}
|
||||
|
||||
fun setPatchEnabled(patch: KnownPatch, enabled: Boolean) {
|
||||
fun closure(seed: KnownPatch, step: (KnownPatch) -> List<KnownPatch>): Set<KnownPatch> =
|
||||
// The accordion renders from [specs]. It defaults to the compiled-in (localized) list and is
|
||||
// rebuilt from a custom zip's manifest.json whenever a custom patch set is selected. (Rminder for others about the new custom patch selection flow)
|
||||
|
||||
private val builtinSpecs: List<PatchSpec> = builtinPatchSpecs(context, json)
|
||||
|
||||
var specs by mutableStateOf(builtinSpecs)
|
||||
private set
|
||||
|
||||
var specsLoading by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
var patchStates by mutableStateOf(prefilledOptions.patchStates)
|
||||
private set
|
||||
|
||||
var selectedVariants by mutableStateOf(prefilledOptions.selectedVariants)
|
||||
private set
|
||||
|
||||
fun variantIndex(spec: PatchSpec): Int =
|
||||
(selectedVariants[spec.id] ?: spec.defaultVariantIndex)
|
||||
.coerceIn(0, spec.variants.lastIndex.coerceAtLeast(0))
|
||||
|
||||
fun isPatchEnabled(spec: PatchSpec): Boolean =
|
||||
!isBlockedByPackageName(spec) && (patchStates[spec.id] ?: spec.defaultEnabled)
|
||||
|
||||
fun setPatchEnabled(spec: PatchSpec, enabled: Boolean) {
|
||||
val byId = specs.associateBy { it.id }
|
||||
fun closure(seed: PatchSpec, step: (PatchSpec) -> List<PatchSpec>): Set<PatchSpec> =
|
||||
buildSet {
|
||||
fun walk(p: KnownPatch) { if (add(p)) step(p).forEach(::walk) }
|
||||
fun walk(p: PatchSpec) { if (add(p)) step(p).forEach(::walk) }
|
||||
walk(seed)
|
||||
}
|
||||
|
||||
val enableUnits: Set<KnownPatch>
|
||||
val disableUnits: Set<KnownPatch>
|
||||
val requiresOf = { p: PatchSpec -> p.requires.mapNotNull(byId::get) }
|
||||
val dependentsOf = { p: PatchSpec -> specs.filter { p.id in it.requires } }
|
||||
|
||||
val enableUnits: Set<PatchSpec>
|
||||
val disableUnits: Set<PatchSpec>
|
||||
if (enabled) {
|
||||
enableUnits = closure(patch) { it.requires }
|
||||
enableUnits = closure(spec, requiresOf)
|
||||
disableUnits = enableUnits.flatMap { it.disables }
|
||||
.flatMapTo(mutableSetOf()) { d ->
|
||||
closure(d) { dep -> KnownPatch.All.filter { dep in it.requires } }
|
||||
}
|
||||
.mapNotNull(byId::get)
|
||||
.flatMapTo(mutableSetOf()) { d -> closure(d, dependentsOf) }
|
||||
} else {
|
||||
enableUnits = emptySet()
|
||||
disableUnits = closure(patch) { p -> KnownPatch.All.filter { p in it.requires } }
|
||||
disableUnits = closure(spec, dependentsOf)
|
||||
}
|
||||
|
||||
val enableFiles = enableUnits.flatMap { it.fileNames }.toSet()
|
||||
val disableFiles = disableUnits.flatMap { it.fileNames }.toSet()
|
||||
disabledPatches = (disabledPatches - enableFiles) + disableFiles
|
||||
patchStates = patchStates.toMutableMap().apply {
|
||||
enableUnits.forEach { this[it.id] = true }
|
||||
disableUnits.forEach { this[it.id] = false }
|
||||
}
|
||||
}
|
||||
|
||||
val enabledPatchCount: Int
|
||||
get() = KnownPatch.All.count { isPatchEnabled(it) }
|
||||
fun selectVariant(spec: PatchSpec, index: Int) {
|
||||
if (spec.variants.isEmpty() || index !in spec.variants.indices) return
|
||||
selectedVariants = selectedVariants + (spec.id to index)
|
||||
}
|
||||
|
||||
// Advanced (per-patch) options
|
||||
// Values are seeded from the prefilled config, edited here, and written back out in
|
||||
// generateConfig(). At patch time PatchOptions.smaliSubstitutions() turns slider values into
|
||||
// the literals baked into the matching `.patch` files (see SmaliPatchStep). ()
|
||||
|
||||
private var optionBools by mutableStateOf(prefilledOptions.optionBools)
|
||||
private var optionFloats by mutableStateOf(prefilledOptions.optionFloats)
|
||||
private var optionInts by mutableStateOf(prefilledOptions.optionInts)
|
||||
|
||||
private fun keyOf(spec: PatchSpec, option: OptionSpec): String = "${spec.id}/${option.key}"
|
||||
|
||||
fun toggleValue(spec: PatchSpec, option: OptionSpec.Toggle): Boolean =
|
||||
optionBools[keyOf(spec, option)] ?: option.default
|
||||
|
||||
fun setToggleValue(spec: PatchSpec, option: OptionSpec.Toggle, value: Boolean) {
|
||||
optionBools = optionBools + (keyOf(spec, option) to value)
|
||||
}
|
||||
|
||||
fun sliderValue(spec: PatchSpec, option: OptionSpec.Slider): Float =
|
||||
(optionFloats[keyOf(spec, option)] ?: option.default).coerceIn(option.min, option.max)
|
||||
|
||||
fun setSliderValue(spec: PatchSpec, option: OptionSpec.Slider, value: Float) {
|
||||
optionFloats = optionFloats + (keyOf(spec, option) to value)
|
||||
}
|
||||
|
||||
fun choiceValue(spec: PatchSpec, option: OptionSpec.Choice): Int =
|
||||
(optionInts[keyOf(spec, option)] ?: option.defaultIndex)
|
||||
.coerceIn(0, option.entries.lastIndex.coerceAtLeast(0))
|
||||
|
||||
fun setChoiceValue(spec: PatchSpec, option: OptionSpec.Choice, index: Int) {
|
||||
if (index !in option.entries.indices) return
|
||||
optionInts = optionInts + (keyOf(spec, option) to index)
|
||||
}
|
||||
|
||||
fun colorValue(spec: PatchSpec, option: OptionSpec.Color): Int =
|
||||
optionInts[keyOf(spec, option)] ?: option.default
|
||||
|
||||
fun setColorValue(spec: PatchSpec, option: OptionSpec.Color, value: Int) {
|
||||
optionInts = optionInts + (keyOf(spec, option) to value)
|
||||
}
|
||||
|
||||
fun isAdvancedModified(spec: PatchSpec): Boolean = spec.advancedOptions.any { option ->
|
||||
when (option) {
|
||||
is OptionSpec.Toggle -> toggleValue(spec, option) != option.default
|
||||
is OptionSpec.Slider -> sliderValue(spec, option) != option.default
|
||||
is OptionSpec.Choice -> choiceValue(spec, option) != option.defaultIndex
|
||||
is OptionSpec.Color -> colorValue(spec, option) != option.default
|
||||
}
|
||||
}
|
||||
|
||||
fun resetAdvanced(spec: PatchSpec) {
|
||||
val prefix = "${spec.id}/"
|
||||
optionBools = optionBools.filterKeys { !it.startsWith(prefix) }
|
||||
optionFloats = optionFloats.filterKeys { !it.startsWith(prefix) }
|
||||
optionInts = optionInts.filterKeys { !it.startsWith(prefix) }
|
||||
}
|
||||
|
||||
val optionState: PatchOptionState = PatchOptionState(
|
||||
toggle = ::toggleValue,
|
||||
setToggle = ::setToggleValue,
|
||||
slider = ::sliderValue,
|
||||
setSlider = ::setSliderValue,
|
||||
choice = ::choiceValue,
|
||||
setChoice = ::setChoiceValue,
|
||||
color = ::colorValue,
|
||||
setColor = ::setColorValue,
|
||||
isModified = ::isAdvancedModified,
|
||||
reset = ::resetAdvanced,
|
||||
)
|
||||
|
||||
fun lockState(spec: PatchSpec): PatchLock {
|
||||
if (isBlockedByPackageName(spec)) return PatchLock.RequiresDefaultPackage
|
||||
if (spec.variants.isNotEmpty()) return PatchLock.Free
|
||||
|
||||
val byId = specs.associateBy { it.id }
|
||||
fun closure(seed: PatchSpec, step: (PatchSpec) -> List<PatchSpec>): Set<PatchSpec> =
|
||||
buildSet {
|
||||
fun walk(p: PatchSpec) { if (add(p)) step(p).forEach(::walk) }
|
||||
walk(seed)
|
||||
}
|
||||
|
||||
val requiresOf = { p: PatchSpec -> p.requires.mapNotNull(byId::get) }
|
||||
val dependentsOf = { p: PatchSpec -> specs.filter { p.id in it.requires } }
|
||||
|
||||
for (other in specs) {
|
||||
if (other.id == spec.id || !isPatchEnabled(other)) continue
|
||||
|
||||
val requiresClosure = closure(other, requiresOf)
|
||||
if (spec in requiresClosure - other) return PatchLock.LockedOn(other)
|
||||
|
||||
val disablesClosure = requiresClosure.flatMap { it.disables }
|
||||
.mapNotNull(byId::get)
|
||||
.flatMapTo(mutableSetOf()) { d -> closure(d, dependentsOf) }
|
||||
if (spec in disablesClosure) return PatchLock.LockedOff(other)
|
||||
}
|
||||
return PatchLock.Free
|
||||
}
|
||||
|
||||
var customTidalApk by mutableStateOf<PatchComponent?>(null)
|
||||
private set
|
||||
@@ -102,6 +266,89 @@ class PatchOptionsModel(
|
||||
componentType = PatchComponent.Type.Patches,
|
||||
)
|
||||
)
|
||||
reloadSpecs(customPatches)
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds [specs] from the selected patch set
|
||||
*/
|
||||
private fun reloadSpecs(component: PatchComponent?) = screenModelScope.launchIO {
|
||||
mainThread { specsLoading = true }
|
||||
val loaded = if (component == null) builtinSpecs else loadManifestSpecs(component) ?: builtinSpecs
|
||||
mainThread {
|
||||
specs = loaded
|
||||
validatePatchSelection()
|
||||
specsLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadManifestSpecs(component: PatchComponent): List<PatchSpec>? =
|
||||
loadManifestSpecs(component.getFile(paths))
|
||||
|
||||
private fun loadManifestSpecs(file: File): List<PatchSpec>? {
|
||||
if (!file.exists()) return null
|
||||
return try {
|
||||
val bytes = ZipReader(file).use { it.openEntry(MANIFEST_NAME)?.read() } ?: return null
|
||||
json.decodeFromString(PatchManifest.serializer(), bytes.decodeToString())
|
||||
.patches
|
||||
.sortedBy { it.order } // manifest is authoritative for UI order (not KnownPatch)
|
||||
.takeIf { it.isNotEmpty() }
|
||||
} catch (t: Throwable) {
|
||||
Log.w(BuildConfig.TAG, "Failed to parse $MANIFEST_NAME; using built-in list", t)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls the patch list from the latest GitHub release's patches.zip manifest
|
||||
*/
|
||||
private suspend fun loadLatestReleaseSpecs(): List<PatchSpec>? = try {
|
||||
val release = github.getLatestRelease().getOrThrow()
|
||||
val url = release.assets
|
||||
.find { it.name == RadiantLyricsGithubService.PATCHES_ASSET_NAME }
|
||||
?.browserDownloadUrl
|
||||
if (url == null) {
|
||||
Log.w(BuildConfig.TAG, "Latest release ${release.tagName} has no patches.zip asset; using built-in list")
|
||||
null
|
||||
} else {
|
||||
// Cached per release tag so re-opening the screen doesn't re-download the same zip.
|
||||
val dest = paths.cacheDownloadDir.resolve("manifest-${release.tagName}.zip")
|
||||
.apply { parentFile?.mkdirs() }
|
||||
if (dest.exists() || downloader.download(url, dest) is IDownloadManager.Result.Success) {
|
||||
loadManifestSpecs(dest).also { loaded ->
|
||||
if (loaded != null) {
|
||||
Log.i(BuildConfig.TAG, "Loaded ${loaded.size} patches from latest release ${release.tagName} manifest")
|
||||
} else {
|
||||
Log.w(BuildConfig.TAG, "Latest release ${release.tagName} ships no manifest.json; using built-in list")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
Log.w(BuildConfig.TAG, "Failed to load latest release manifest; using built-in list", t)
|
||||
null
|
||||
}
|
||||
|
||||
private fun loadCachedReleaseManifestSpecs(): List<PatchSpec>? =
|
||||
paths.cacheDownloadDir
|
||||
.listFiles { f -> f.name.startsWith("manifest-") && f.name.endsWith(".zip") }
|
||||
?.maxByOrNull { it.lastModified() }
|
||||
?.let { loadManifestSpecs(it) }
|
||||
?.also { Log.i(BuildConfig.TAG, "Loaded ${it.size} patches from cached release manifest (offline)") }
|
||||
|
||||
/** Default (non-custom) source: latest release manifest → last cached manifest → built-in list. */
|
||||
private fun loadDefaultSpecs() = screenModelScope.launchIO {
|
||||
mainThread { specsLoading = true }
|
||||
// Only hit the network when actually online
|
||||
val loaded = (if (context.isOnline()) loadLatestReleaseSpecs() else null)
|
||||
?: loadCachedReleaseManifestSpecs() ?: builtinSpecs
|
||||
mainThread {
|
||||
specs = loaded
|
||||
validatePatchSelection()
|
||||
specsLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
val isConfigValid by derivedStateOf {
|
||||
@@ -122,7 +369,14 @@ class PatchOptionsModel(
|
||||
debuggable = debuggable,
|
||||
customTidalApk = customTidalApk,
|
||||
customPatches = customPatches,
|
||||
disabledPatches = disabledPatches,
|
||||
// Hard-disable package-name-bound patches so the patcher can't apply
|
||||
patchStates = patchStates.toMutableMap().apply {
|
||||
specs.filter { isBlockedByPackageName(it) }.forEach { this[it.id] = false }
|
||||
},
|
||||
selectedVariants = selectedVariants,
|
||||
optionFloats = optionFloats,
|
||||
optionBools = optionBools,
|
||||
optionInts = optionInts,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -147,11 +401,24 @@ class PatchOptionsModel(
|
||||
mainThread { packageNameState = state }
|
||||
}
|
||||
|
||||
private fun validatePatchSelection() {
|
||||
for (spec in specs) {
|
||||
if (isPatchEnabled(spec)) {
|
||||
setPatchEnabled(spec, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
validatePatchSelection()
|
||||
screenModelScope.launchBlock { fetchPkgNameState() }
|
||||
// Default source is the latest release's manifest; custom selections drive themselves.
|
||||
if (customPatches == null) loadDefaultSpecs()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MANIFEST_NAME = "manifest.json"
|
||||
|
||||
private val PACKAGE_REGEX = """^[a-z]\w*(\.[a-z]\w*)+$"""
|
||||
.toRegex(RegexOption.IGNORE_CASE)
|
||||
}
|
||||
@@ -162,3 +429,12 @@ enum class PackageNameState {
|
||||
Invalid,
|
||||
Taken,
|
||||
}
|
||||
|
||||
sealed class PatchLock {
|
||||
data object Free : PatchLock()
|
||||
data class LockedOn(val by: PatchSpec) : PatchLock()
|
||||
data class LockedOff(val by: PatchSpec) : PatchLock()
|
||||
|
||||
/** Locked off because the install uses a non-default package name */
|
||||
data object RequiresDefaultPackage : PatchLock()
|
||||
}
|
||||
|
||||
+82
-7
@@ -19,7 +19,9 @@ import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.components.*
|
||||
import com.meowarex.rlmobile.ui.screens.componentopts.PatchComponent
|
||||
import com.meowarex.rlmobile.ui.screens.patching.PatchingScreen
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.components.PackageNameStateLabel
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.components.PackageNameUnlockDialog
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.components.PatchOptionsAppBar
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.components.PatchSelectionAccordion
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.components.options.*
|
||||
@@ -46,6 +48,8 @@ class PatchOptionsScreen(
|
||||
|
||||
debuggable = model.debuggable,
|
||||
setDebuggable = model::changeDebuggable,
|
||||
bypassIncompatible = model.bypassIncompatible,
|
||||
setBypassIncompatible = model::changeBypassIncompatible,
|
||||
|
||||
appName = model.appName,
|
||||
appNameIsError = model.appNameIsError,
|
||||
@@ -55,14 +59,22 @@ class PatchOptionsScreen(
|
||||
packageNameState = model.packageNameState,
|
||||
setPackageName = model::changePackageName,
|
||||
|
||||
packageNameLocked = model.packageNameLocked,
|
||||
onUnlockPackageName = model::unlockPackageName,
|
||||
onLockPackageName = model::lockPackageName,
|
||||
|
||||
customTidalApk = model.customTidalApk,
|
||||
customPatches = model.customPatches,
|
||||
onSelectCustomTidalApk = { model.selectCustomTidalApk(navigator) },
|
||||
onSelectCustomPatches = { model.selectCustomPatches(navigator) },
|
||||
|
||||
enabledPatchCount = model.enabledPatchCount,
|
||||
specs = model.specs,
|
||||
isPatchEnabled = model::isPatchEnabled,
|
||||
onTogglePatch = model::setPatchEnabled,
|
||||
patchLockState = model::lockState,
|
||||
variantIndex = model::variantIndex,
|
||||
onSelectVariant = model::selectVariant,
|
||||
optionState = model.optionState,
|
||||
|
||||
isConfigValid = model.isConfigValid,
|
||||
onInstall = {
|
||||
@@ -79,6 +91,8 @@ fun PatchOptionsScreenContent(
|
||||
|
||||
debuggable: Boolean,
|
||||
setDebuggable: (Boolean) -> Unit,
|
||||
bypassIncompatible: Boolean,
|
||||
setBypassIncompatible: (Boolean) -> Unit,
|
||||
|
||||
appName: String,
|
||||
appNameIsError: Boolean,
|
||||
@@ -88,18 +102,39 @@ fun PatchOptionsScreenContent(
|
||||
packageNameState: PackageNameState,
|
||||
setPackageName: (String) -> Unit,
|
||||
|
||||
packageNameLocked: Boolean,
|
||||
onUnlockPackageName: () -> Unit,
|
||||
onLockPackageName: () -> Unit,
|
||||
|
||||
customTidalApk: PatchComponent?,
|
||||
onSelectCustomTidalApk: () -> Unit,
|
||||
customPatches: PatchComponent?,
|
||||
onSelectCustomPatches: () -> Unit,
|
||||
|
||||
enabledPatchCount: Int,
|
||||
isPatchEnabled: (KnownPatch) -> Boolean,
|
||||
onTogglePatch: (KnownPatch, Boolean) -> Unit,
|
||||
specs: List<PatchSpec>,
|
||||
isPatchEnabled: (PatchSpec) -> Boolean,
|
||||
onTogglePatch: (PatchSpec, Boolean) -> Unit,
|
||||
patchLockState: (PatchSpec) -> PatchLock,
|
||||
variantIndex: (PatchSpec) -> Int,
|
||||
onSelectVariant: (PatchSpec, Int) -> Unit,
|
||||
optionState: PatchOptionState,
|
||||
|
||||
isConfigValid: Boolean,
|
||||
onInstall: () -> Unit,
|
||||
) {
|
||||
var showPkgUnlockDialog by rememberSaveable { mutableStateOf(false) }
|
||||
if (showPkgUnlockDialog) {
|
||||
PackageNameUnlockDialog(
|
||||
// With "Bypass Incompatible" on, path-gated patches are force-applied, not disabled.
|
||||
blockedTitles = if (bypassIncompatible) emptyList() else specs.filter { it.pathLocked }.map { it.title },
|
||||
onConfirm = {
|
||||
showPkgUnlockDialog = false
|
||||
onUnlockPackageName()
|
||||
},
|
||||
onDismiss = { showPkgUnlockDialog = false },
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = { PatchOptionsAppBar(isUpdate = isUpdate) },
|
||||
) { paddingValues ->
|
||||
@@ -149,6 +184,10 @@ fun PatchOptionsScreenContent(
|
||||
valueIsDefault = packageNameIsDefault,
|
||||
onValueChange = setPackageName,
|
||||
onValueReset = { setPackageName(PatchOptions.Default.packageName) },
|
||||
locked = packageNameLocked,
|
||||
onLockClick = {
|
||||
if (packageNameLocked) showPkgUnlockDialog = true else onLockPackageName()
|
||||
},
|
||||
) {
|
||||
PackageNameStateLabel(
|
||||
state = packageNameState,
|
||||
@@ -157,12 +196,40 @@ fun PatchOptionsScreenContent(
|
||||
}
|
||||
}
|
||||
|
||||
val (integrations, patches) = remember(specs) { specs.partition { it.isIntegration } }
|
||||
|
||||
if (integrations.isNotEmpty()) {
|
||||
PatchSelectionAccordion(
|
||||
iconRes = R.drawable.ic_extension,
|
||||
titleRes = R.string.patchopts_integrations_title,
|
||||
descriptionRes = R.string.patchopts_integrations_desc,
|
||||
specs = integrations,
|
||||
enabledCount = integrations.count(isPatchEnabled),
|
||||
totalCount = integrations.size,
|
||||
isEnabled = isPatchEnabled,
|
||||
onToggle = onTogglePatch,
|
||||
lockState = patchLockState,
|
||||
variantIndex = variantIndex,
|
||||
onSelectVariant = onSelectVariant,
|
||||
optionState = optionState,
|
||||
modifier = Modifier.padding(top = 4.dp),
|
||||
)
|
||||
}
|
||||
|
||||
PatchSelectionAccordion(
|
||||
enabledCount = enabledPatchCount,
|
||||
totalCount = KnownPatch.All.size,
|
||||
iconRes = R.drawable.ic_healing,
|
||||
titleRes = R.string.patchopts_patches_title,
|
||||
descriptionRes = R.string.patchopts_patches_desc,
|
||||
specs = patches,
|
||||
enabledCount = patches.count(isPatchEnabled),
|
||||
totalCount = patches.size,
|
||||
isEnabled = isPatchEnabled,
|
||||
onToggle = onTogglePatch,
|
||||
modifier = Modifier.padding(top = 4.dp),
|
||||
lockState = patchLockState,
|
||||
variantIndex = variantIndex,
|
||||
onSelectVariant = onSelectVariant,
|
||||
optionState = optionState,
|
||||
modifier = if (integrations.isEmpty()) Modifier.padding(top = 4.dp) else Modifier,
|
||||
)
|
||||
|
||||
if (isDevMode) {
|
||||
@@ -206,6 +273,14 @@ fun PatchOptionsScreenContent(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SwitchPatchOption(
|
||||
icon = painterResource(R.drawable.ic_lock_open),
|
||||
name = stringResource(R.string.patchopts_bypass_incompatible_title),
|
||||
description = stringResource(R.string.patchopts_bypass_incompatible_desc),
|
||||
value = bypassIncompatible,
|
||||
onValueChange = setBypassIncompatible,
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Serializable
|
||||
data class PatchManifest(
|
||||
val version: Int = 1,
|
||||
val patches: List<PatchSpec> = emptyList(),
|
||||
)
|
||||
|
||||
@Immutable
|
||||
@Serializable
|
||||
data class PatchSpec(
|
||||
val id: String,
|
||||
val order: Int = 0,
|
||||
val fileNames: List<String> = emptyList(),
|
||||
val extensionFiles: List<String> = emptyList(),
|
||||
val title: String,
|
||||
val description: String = "",
|
||||
@SerialName("default") val defaultEnabled: Boolean = false,
|
||||
/** ids of patches that get force-enabled when this one is enabled. */
|
||||
val requires: List<String> = emptyList(),
|
||||
/** ids of patches that get force-disabled when this one is enabled. */
|
||||
val disables: List<String> = emptyList(),
|
||||
val variants: List<VariantSpec> = emptyList(),
|
||||
val defaultVariantIndex: Int = 0,
|
||||
val advancedOptions: List<OptionSpec> = emptyList(),
|
||||
val category: String = CATEGORY_PATCH,
|
||||
val pathLocked: Boolean = false,
|
||||
) {
|
||||
val isIntegration: Boolean get() = category == CATEGORY_INTEGRATION
|
||||
|
||||
companion object {
|
||||
const val CATEGORY_PATCH = "patch"
|
||||
const val CATEGORY_INTEGRATION = "integration"
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@Serializable
|
||||
data class VariantSpec(
|
||||
val title: String,
|
||||
val fileNames: List<String> = emptyList(),
|
||||
val extensionFiles: List<String> = emptyList(),
|
||||
val enabled: Boolean = true,
|
||||
)
|
||||
|
||||
/** A single advanced option. Discriminated by `"type"` in JSON. */
|
||||
@Serializable
|
||||
sealed interface OptionSpec {
|
||||
val key: String
|
||||
val title: String
|
||||
val description: String
|
||||
|
||||
@Immutable
|
||||
@Serializable
|
||||
@SerialName("toggle")
|
||||
data class Toggle(
|
||||
override val key: String,
|
||||
override val title: String,
|
||||
override val description: String = "",
|
||||
val default: Boolean = false,
|
||||
/** Patch files applied only while this toggle is on (and its patch is enabled). */
|
||||
val fileNames: List<String> = emptyList(),
|
||||
/** Helper smali extracted only while this toggle is on. */
|
||||
val extensionFiles: List<String> = emptyList(),
|
||||
/** Render inline beneath the variant selector instead of inside the advanced sheet. */
|
||||
val inline: Boolean = false,
|
||||
/** Greyed out (with the lock dialog) unless this variant index is the selected one. */
|
||||
val requiresVariant: Int? = null,
|
||||
/** Greyed out unless the sibling option with this key is currently on. */
|
||||
val requiresOption: String? = null,
|
||||
/** Variant indices hidden from the picker while this toggle is on. */
|
||||
val hidesVariants: List<Int> = emptyList(),
|
||||
/** Variant title overrides (index -> title) applied while this toggle is on. */
|
||||
val relabelVariants: Map<Int, String> = emptyMap(),
|
||||
/** Placeholder name (without the surrounding `__`) baked into the `.patch` files. */
|
||||
val token: String? = null,
|
||||
) : OptionSpec
|
||||
|
||||
@Immutable
|
||||
@Serializable
|
||||
@SerialName("slider")
|
||||
data class Slider(
|
||||
override val key: String,
|
||||
override val title: String,
|
||||
override val description: String = "",
|
||||
val default: Float = 0f,
|
||||
val min: Float = 0f,
|
||||
val max: Float = 100f,
|
||||
val steps: Int = 0,
|
||||
val displayAsPercent: Boolean = false,
|
||||
val unit: String? = null,
|
||||
/** Placeholder name (without the surrounding `__`) baked into the `.patch` files. */
|
||||
val token: String? = null,
|
||||
/** How the chosen value becomes the smali literal that replaces the token. */
|
||||
val encode: SmaliEncode? = null,
|
||||
) : OptionSpec {
|
||||
val valueRange: ClosedFloatingPointRange<Float> get() = min..max
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@Serializable
|
||||
@SerialName("choice")
|
||||
data class Choice(
|
||||
override val key: String,
|
||||
override val title: String = "",
|
||||
override val description: String = "",
|
||||
val entries: List<String> = emptyList(),
|
||||
val defaultIndex: Int = 0,
|
||||
val values: List<String> = emptyList(),
|
||||
val requiresOption: String? = null,
|
||||
val token: String? = null,
|
||||
) : OptionSpec
|
||||
|
||||
@Immutable
|
||||
@Serializable
|
||||
@SerialName("color")
|
||||
data class Color(
|
||||
override val key: String,
|
||||
override val title: String = "",
|
||||
override val description: String = "",
|
||||
val default: Int = 0,
|
||||
/** Placeholder name (without the surrounding `__`) baked into the `.patch`/extension files. */
|
||||
val token: String? = null,
|
||||
) : OptionSpec
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class EncodeKind {
|
||||
@SerialName("floatBits")
|
||||
FloatBits,
|
||||
|
||||
@SerialName("argbAlpha")
|
||||
ArgbAlpha,
|
||||
|
||||
@SerialName("int")
|
||||
IntValue,
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@Serializable
|
||||
data class SmaliEncode(
|
||||
val kind: EncodeKind,
|
||||
val scale: Float = 1f,
|
||||
) {
|
||||
fun encode(value: Float): String = when (kind) {
|
||||
// Float dp bit-pattern: const vX, <bits>; Dp.constructor-impl(F)F
|
||||
EncodeKind.FloatBits -> (value * scale).toRawBits().toString()
|
||||
// ARGB int with black RGB and a percentage-derived alpha: const vX, <argb>
|
||||
EncodeKind.ArgbAlpha -> ((value / 100f * 255f).roundToInt() shl 24).toString()
|
||||
// Plain (optionally scaled) integer literal.
|
||||
EncodeKind.IntValue -> (value * scale).roundToInt().toString()
|
||||
}
|
||||
}
|
||||
|
||||
/** Asset filename of the bundled fallback manifest snapshot (a copy of the release manifest.json). */
|
||||
const val BUNDLED_MANIFEST_ASSET = "patches-manifest.json"
|
||||
|
||||
// Fallback patch list (snapshot of the release manifest bundled in the app's assets)
|
||||
fun builtinPatchSpecs(context: Context, json: Json): List<PatchSpec> = try {
|
||||
context.assets.open(BUNDLED_MANIFEST_ASSET).use { it.readBytes() }
|
||||
.let { json.decodeFromString(PatchManifest.serializer(), it.decodeToString()) }
|
||||
.patches
|
||||
.sortedBy { it.order }
|
||||
} catch (t: Throwable) {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class EffectiveVariant(val originalIndex: Int, val title: String, val enabled: Boolean = true)
|
||||
|
||||
fun PatchSpec.effectiveVariants(isOptionOn: (OptionSpec.Toggle) -> Boolean): List<EffectiveVariant> {
|
||||
if (variants.isEmpty()) return emptyList()
|
||||
val hidden = mutableSetOf<Int>()
|
||||
val relabel = mutableMapOf<Int, String>()
|
||||
for (option in advancedOptions) {
|
||||
if (option is OptionSpec.Toggle && isOptionOn(option)) {
|
||||
hidden += option.hidesVariants
|
||||
relabel += option.relabelVariants
|
||||
}
|
||||
}
|
||||
return variants.mapIndexedNotNull { index, variant ->
|
||||
if (index in hidden) null
|
||||
else EffectiveVariant(index, relabel[index] ?: variant.title, variant.enabled)
|
||||
}
|
||||
}
|
||||
|
||||
fun PatchSpec.resolveVariantIndex(stored: Int, isOptionOn: (OptionSpec.Toggle) -> Boolean): Int {
|
||||
val visible = effectiveVariants(isOptionOn)
|
||||
// never resolve onto a greyed/disabled variant
|
||||
if (visible.isEmpty() || visible.any { it.originalIndex == stored && it.enabled }) return stored
|
||||
val relabelKeys = buildSet {
|
||||
for (option in advancedOptions) {
|
||||
if (option is OptionSpec.Toggle && isOptionOn(option)) addAll(option.relabelVariants.keys)
|
||||
}
|
||||
}
|
||||
return visible.firstOrNull { it.originalIndex in relabelKeys && it.enabled }?.originalIndex
|
||||
?: visible.firstOrNull { it.enabled }?.originalIndex
|
||||
?: visible.first().originalIndex
|
||||
}
|
||||
|
||||
sealed interface OptionLock {
|
||||
data object Free : OptionLock
|
||||
data class NeedsVariant(val title: String) : OptionLock
|
||||
data class NeedsOption(val title: String) : OptionLock
|
||||
}
|
||||
|
||||
fun PatchSpec.optionLock(
|
||||
option: OptionSpec,
|
||||
selectedVariant: Int,
|
||||
isOptionOn: (OptionSpec.Toggle) -> Boolean,
|
||||
): OptionLock {
|
||||
if (option !is OptionSpec.Toggle) return OptionLock.Free
|
||||
option.requiresVariant?.let { required ->
|
||||
if (selectedVariant != required) {
|
||||
variants.getOrNull(required)?.let { return OptionLock.NeedsVariant(it.title) }
|
||||
}
|
||||
}
|
||||
option.requiresOption?.let { key ->
|
||||
val required = advancedOptions.filterIsInstance<OptionSpec.Toggle>().firstOrNull { it.key == key }
|
||||
if (required != null && !isOptionOn(required)) return OptionLock.NeedsOption(required.title)
|
||||
}
|
||||
return OptionLock.Free
|
||||
}
|
||||
|
||||
@Stable
|
||||
class PatchOptionState(
|
||||
val toggle: (PatchSpec, OptionSpec.Toggle) -> Boolean,
|
||||
val setToggle: (PatchSpec, OptionSpec.Toggle, Boolean) -> Unit,
|
||||
val slider: (PatchSpec, OptionSpec.Slider) -> Float,
|
||||
val setSlider: (PatchSpec, OptionSpec.Slider, Float) -> Unit,
|
||||
val choice: (PatchSpec, OptionSpec.Choice) -> Int,
|
||||
val setChoice: (PatchSpec, OptionSpec.Choice, Int) -> Unit,
|
||||
val color: (PatchSpec, OptionSpec.Color) -> Int,
|
||||
val setColor: (PatchSpec, OptionSpec.Color, Int) -> Unit,
|
||||
val isModified: (PatchSpec) -> Boolean,
|
||||
val reset: (PatchSpec) -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
/** Read-only stub returning defaults — for Compose previews. */
|
||||
val Preview = PatchOptionState(
|
||||
toggle = { _, option -> option.default },
|
||||
setToggle = { _, _, _ -> },
|
||||
slider = { _, option -> option.default },
|
||||
setSlider = { _, _, _ -> },
|
||||
choice = { _, option -> option.defaultIndex },
|
||||
setChoice = { _, _, _ -> },
|
||||
color = { _, option -> option.default },
|
||||
setColor = { _, _, _ -> },
|
||||
isModified = { false },
|
||||
reset = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts.components
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.fromHtml
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
||||
|
||||
/**
|
||||
* Confirms unlocking the package-name field, warning that a custom name
|
||||
* blocks the listed patches/integrations from working.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PackageNameUnlockDialog(
|
||||
blockedTitles: List<String>,
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
BasicAlertDialog(onDismissRequest = onDismiss) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.large,
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
tonalElevation = 6.dp,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 24.dp, end = 12.dp, top = 20.dp, bottom = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patchopts_pkgname_warning_title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
Text(
|
||||
text = AnnotatedString.fromHtml(
|
||||
stringResource(R.string.patchopts_pkgname_warning_msg, PatchOptions.Default.packageName),
|
||||
),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
if (blockedTitles.isNotEmpty()) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
) {
|
||||
for (title in blockedTitles) {
|
||||
Text(
|
||||
text = "• $title",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = stringResource(R.string.patchopts_pkgname_warning_permanent),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(modifier = Modifier.align(Alignment.CenterEnd)) {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.action_cancel))
|
||||
}
|
||||
TextButton(onClick = onConfirm) {
|
||||
Text(stringResource(R.string.action_continue))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+682
@@ -0,0 +1,682 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.fromHtml
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.components.ResetToDefaultButton
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.OptionLock
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.OptionSpec
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionState
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.optionLock
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchSpec
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PatchAdvancedOptionsSheet(
|
||||
patch: PatchSpec,
|
||||
state: PatchOptionState,
|
||||
selectedVariant: Int,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val scope = rememberCoroutineScope()
|
||||
var lockDialog by remember { mutableStateOf<OptionLock?>(null) }
|
||||
|
||||
fun dismiss() {
|
||||
scope.launch { sheetState.hide() }.invokeOnCompletion {
|
||||
if (!sheetState.isVisible) onDismiss()
|
||||
}
|
||||
}
|
||||
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = onDismiss,
|
||||
sheetState = sheetState,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 24.dp)
|
||||
.padding(bottom = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(20.dp),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patchopts_advanced_sheet_label),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Text(
|
||||
text = patch.title,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
)
|
||||
}
|
||||
|
||||
ResetToDefaultButton(
|
||||
enabled = state.isModified(patch),
|
||||
onClick = { state.reset(patch) },
|
||||
)
|
||||
}
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
val parentKeys = patch.advancedOptions
|
||||
.filterIsInstance<OptionSpec.Toggle>()
|
||||
.filter { !it.inline }
|
||||
.map { it.key }
|
||||
.toSet()
|
||||
val sheetOptions = patch.advancedOptions.filter { option ->
|
||||
when {
|
||||
option is OptionSpec.Toggle && option.inline -> false
|
||||
option is OptionSpec.Toggle && option.requiresOption in parentKeys -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
for (option in sheetOptions) key(option.key) {
|
||||
val lock = patch.optionLock(option, selectedVariant) { state.toggle(patch, it) }
|
||||
when (option) {
|
||||
is OptionSpec.Toggle -> {
|
||||
val toggleOn = state.toggle(patch, option)
|
||||
val gatedChoices = patch.advancedOptions.filterIsInstance<OptionSpec.Choice>()
|
||||
.filter { it.requiresOption == option.key }
|
||||
val gatedToggles = patch.advancedOptions.filterIsInstance<OptionSpec.Toggle>()
|
||||
.filter { it.requiresOption == option.key && !it.inline }
|
||||
val hasSubOptions = gatedChoices.isNotEmpty() || gatedToggles.isNotEmpty()
|
||||
val carded = hasSubOptions && toggleOn
|
||||
|
||||
Column(
|
||||
modifier = if (carded) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.primary.copy(alpha = 0.07f))
|
||||
.border(
|
||||
width = 1.5.dp,
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.55f),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
)
|
||||
.padding(horizontal = 12.dp, vertical = 4.dp)
|
||||
} else {
|
||||
Modifier.fillMaxWidth()
|
||||
},
|
||||
) {
|
||||
ToggleOptionRow(
|
||||
title = option.title,
|
||||
description = option.description,
|
||||
checked = toggleOn,
|
||||
onCheckedChange = { state.setToggle(patch, option, it) },
|
||||
locked = lock != OptionLock.Free,
|
||||
onLockedTap = { lockDialog = lock },
|
||||
modifier = if (hasSubOptions) Modifier.padding(vertical = 6.dp) else Modifier,
|
||||
)
|
||||
if (hasSubOptions) {
|
||||
AnimatedVisibility(visible = toggleOn) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 8.dp),
|
||||
) {
|
||||
gatedChoices.forEach { choice ->
|
||||
key(choice.key) {
|
||||
ChoiceOptionRow(
|
||||
title = choice.title,
|
||||
entries = choice.entries,
|
||||
selectedIndex = state.choice(patch, choice),
|
||||
onSelect = { state.setChoice(patch, choice, it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
gatedToggles.forEach { sub ->
|
||||
key(sub.key) {
|
||||
val subLock = patch.optionLock(sub, selectedVariant) { state.toggle(patch, it) }
|
||||
ToggleOptionRow(
|
||||
title = sub.title,
|
||||
description = sub.description,
|
||||
checked = state.toggle(patch, sub),
|
||||
onCheckedChange = { state.setToggle(patch, sub, it) },
|
||||
locked = subLock != OptionLock.Free,
|
||||
onLockedTap = { lockDialog = subLock },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is OptionSpec.Slider -> SliderOptionRow(
|
||||
title = option.title,
|
||||
description = option.description,
|
||||
value = state.slider(patch, option),
|
||||
valueLabel = formatSliderValue(option, state.slider(patch, option)),
|
||||
valueRange = option.valueRange,
|
||||
steps = option.steps,
|
||||
onValueChange = { state.setSlider(patch, option, it) },
|
||||
)
|
||||
|
||||
is OptionSpec.Choice ->
|
||||
if (option.requiresOption == null) {
|
||||
ChoiceOptionRow(
|
||||
title = option.title,
|
||||
entries = option.entries,
|
||||
selectedIndex = state.choice(patch, option),
|
||||
onSelect = { state.setChoice(patch, option, it) },
|
||||
)
|
||||
}
|
||||
|
||||
is OptionSpec.Color -> ColorOptionRow(
|
||||
title = option.title,
|
||||
color = state.color(patch, option),
|
||||
onColorChange = { state.setColor(patch, option, it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
FilledTonalButton(
|
||||
onClick = { dismiss() },
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
modifier = Modifier
|
||||
.align(Alignment.End)
|
||||
.padding(top = 4.dp),
|
||||
) {
|
||||
Text(stringResource(R.string.action_done))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lockDialog?.let { lock ->
|
||||
OptionLockDialog(lock = lock, onDismiss = { lockDialog = null })
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ToggleOptionRow(
|
||||
title: String,
|
||||
description: String,
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
locked: Boolean = false,
|
||||
onLockedTap: () -> Unit = {},
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val interactionSource = remember(::MutableInteractionSource)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
role = Role.Switch,
|
||||
) { if (locked) onLockedTap() else onCheckedChange(!checked) }
|
||||
.alpha(if (locked) 0.45f else 1f),
|
||||
) {
|
||||
OptionText(
|
||||
title = title,
|
||||
description = description,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
|
||||
Box {
|
||||
Switch(
|
||||
checked = checked,
|
||||
enabled = !locked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
if (locked) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.clickable(
|
||||
interactionSource = remember(::MutableInteractionSource),
|
||||
indication = null,
|
||||
role = Role.Switch,
|
||||
) { onLockedTap() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SliderOptionRow(
|
||||
title: String,
|
||||
description: String,
|
||||
value: Float,
|
||||
valueLabel: String,
|
||||
valueRange: ClosedFloatingPointRange<Float>,
|
||||
steps: Int,
|
||||
onValueChange: (Float) -> Unit,
|
||||
) {
|
||||
// Whether the slider snaps to the step dots
|
||||
var snap by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Text(
|
||||
text = valueLabel,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
if (steps > 0) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
snap = !snap
|
||||
if (snap) onValueChange(snapToNearestStep(value, valueRange, steps))
|
||||
},
|
||||
modifier = Modifier.size(32.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
if (snap) R.drawable.ic_lock else R.drawable.ic_lock_open,
|
||||
),
|
||||
contentDescription = stringResource(R.string.patchopts_toggle_snap),
|
||||
tint = if (snap) MaterialTheme.colorScheme.primary
|
||||
else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = description,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.alpha(.7f),
|
||||
)
|
||||
|
||||
// Locked -> native stepped slider (dots + snapping), left exactly as-is
|
||||
// Unlocked -> plain continuous slider with the trailing stop-indicator dot removed
|
||||
if (snap) {
|
||||
Slider(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
valueRange = valueRange,
|
||||
steps = steps,
|
||||
)
|
||||
} else {
|
||||
Slider(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
valueRange = valueRange,
|
||||
track = { SliderDefaults.Track(sliderState = it, drawStopIndicator = null) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun snapToNearestStep(
|
||||
value: Float,
|
||||
range: ClosedFloatingPointRange<Float>,
|
||||
steps: Int,
|
||||
): Float {
|
||||
if (steps <= 0) return value
|
||||
val stepSize = (range.endInclusive - range.start) / (steps + 1)
|
||||
if (stepSize <= 0f) return value
|
||||
val snapped = range.start + Math.round((value - range.start) / stepSize) * stepSize
|
||||
return snapped.coerceIn(range.start, range.endInclusive)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChoiceOptionRow(
|
||||
entries: List<String>,
|
||||
selectedIndex: Int,
|
||||
onSelect: (Int) -> Unit,
|
||||
title: String = "",
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
if (title.isNotEmpty()) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
if (entries.size > 3) {
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
val selectedLabel = entries.getOrNull(selectedIndex).orEmpty()
|
||||
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
OutlinedButton(
|
||||
onClick = { expanded = true },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = selectedLabel,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
entries.forEachIndexed { index, entry ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(entry) },
|
||||
onClick = {
|
||||
onSelect(index)
|
||||
expanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return@Column
|
||||
}
|
||||
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
||||
entries.forEachIndexed { index, entry ->
|
||||
SegmentedButton(
|
||||
selected = index == selectedIndex,
|
||||
onClick = { onSelect(index) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index = index, count = entries.size),
|
||||
icon = {},
|
||||
label = {
|
||||
Text(
|
||||
text = entry,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColorOptionRow(
|
||||
title: String,
|
||||
color: Int,
|
||||
onColorChange: (Int) -> Unit,
|
||||
) {
|
||||
var showPicker by rememberSaveable { mutableStateOf(false) }
|
||||
val selectedColor = color.withOpaqueAlpha()
|
||||
val materialYouColor = MaterialTheme.colorScheme.primary.toArgb().withOpaqueAlpha()
|
||||
val presets = listOf(
|
||||
stringResource(R.string.patch_waze_color_waze_default),
|
||||
stringResource(R.string.patch_waze_color_tidal_cyan),
|
||||
stringResource(R.string.patch_waze_color_material_you),
|
||||
)
|
||||
val presetColors = listOf(WAZE_DEFAULT, TIDAL_CYAN, materialYouColor)
|
||||
val selectedPreset = presetColors.indexOf(selectedColor)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.clickable { showPicker = true }
|
||||
.padding(vertical = 4.dp),
|
||||
) {
|
||||
ColorSwatch(color = selectedColor)
|
||||
Text(
|
||||
text = selectedColor.toRgbHex(),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
||||
presets.forEachIndexed { index, label ->
|
||||
SegmentedButton(
|
||||
selected = index == selectedPreset,
|
||||
onClick = { onColorChange(presetColors[index]) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index = index, count = presets.size),
|
||||
icon = {},
|
||||
label = {
|
||||
Text(
|
||||
text = label,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showPicker) {
|
||||
ColorPickerDialog(
|
||||
initialColor = selectedColor,
|
||||
onDismiss = { showPicker = false },
|
||||
onColorSelected = {
|
||||
onColorChange(it)
|
||||
showPicker = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColorSwatch(color: Int, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(32.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Color(color))
|
||||
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, CircleShape),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColorPickerDialog(
|
||||
initialColor: Int,
|
||||
onDismiss: () -> Unit,
|
||||
onColorSelected: (Int) -> Unit,
|
||||
) {
|
||||
var color by rememberSaveable(initialColor) { mutableIntStateOf(initialColor.withOpaqueAlpha()) }
|
||||
var hex by rememberSaveable(initialColor) { mutableStateOf(color.toRgbHex()) }
|
||||
val parsedHex = parseRgbHex(hex)
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.patch_color_picker_title)) },
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
ColorSwatch(color = parsedHex ?: color, modifier = Modifier.size(40.dp))
|
||||
OutlinedTextField(
|
||||
value = hex,
|
||||
onValueChange = { value ->
|
||||
hex = value
|
||||
parseRgbHex(value)?.let { color = it }
|
||||
},
|
||||
label = { Text(stringResource(R.string.patch_color_picker_hex)) },
|
||||
singleLine = true,
|
||||
isError = parsedHex == null,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.Characters,
|
||||
keyboardType = KeyboardType.Ascii,
|
||||
),
|
||||
supportingText = if (parsedHex == null) {
|
||||
{ Text(stringResource(R.string.patch_color_picker_hex_error)) }
|
||||
} else null,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
enabled = parsedHex != null,
|
||||
onClick = { parsedHex?.let(onColorSelected) },
|
||||
) {
|
||||
Text(stringResource(R.string.action_done))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.action_cancel))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OptionText(
|
||||
title: String,
|
||||
description: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = description,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.alpha(.7f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatSliderValue(option: OptionSpec.Slider, value: Float): String {
|
||||
val rounded = value.roundToInt()
|
||||
val unit = option.unit
|
||||
return when {
|
||||
option.displayAsPercent -> "$rounded%"
|
||||
unit != null -> "$rounded $unit"
|
||||
else -> rounded.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private const val OPAQUE_ALPHA = -0x1000000
|
||||
private const val WAZE_DEFAULT = -16747037 // #ff0075e3
|
||||
private const val TIDAL_CYAN = -14549268 // #ff21feec
|
||||
|
||||
private fun Int.withOpaqueAlpha(): Int = (this and 0x00FFFFFF) or OPAQUE_ALPHA
|
||||
|
||||
private fun Int.toRgbHex(): String = "#" +
|
||||
(this and 0x00FFFFFF).toString(16).uppercase().padStart(6, '0')
|
||||
|
||||
private fun parseRgbHex(value: String): Int? {
|
||||
val clean = value.trim()
|
||||
.removePrefix("#")
|
||||
.removePrefix("0x")
|
||||
.removePrefix("0X")
|
||||
if (clean.length != 6 && clean.length != 8) return null
|
||||
val parsed = clean.toLongOrNull(16) ?: return null
|
||||
val rgb = if (clean.length == 8) parsed and 0x00FFFFFFL else parsed
|
||||
return (0xFF000000L or rgb).toInt()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun OptionLockDialog(lock: OptionLock, onDismiss: () -> Unit) {
|
||||
val message = when (lock) {
|
||||
is OptionLock.NeedsVariant -> stringResource(R.string.patch_opt_lock_needs_variant, lock.title)
|
||||
is OptionLock.NeedsOption -> stringResource(R.string.patch_opt_lock_needs_option, lock.title)
|
||||
OptionLock.Free -> return
|
||||
}
|
||||
|
||||
BasicAlertDialog(onDismissRequest = onDismiss) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.large,
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
tonalElevation = 6.dp,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 24.dp, end = 12.dp, top = 20.dp, bottom = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patch_opt_lock_title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
Text(
|
||||
text = AnnotatedString.fromHtml(message),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
TextButton(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
) {
|
||||
Text(stringResource(R.string.action_got_it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+295
-16
@@ -1,12 +1,16 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts.components
|
||||
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
@@ -18,16 +22,34 @@ import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.fromHtml
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.KnownPatch
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.OptionSpec
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchLock
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionState
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchSpec
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.effectiveVariants
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.resolveVariantIndex
|
||||
|
||||
private data class LockInfo(val patch: PatchSpec, val lock: PatchLock)
|
||||
|
||||
@Composable
|
||||
fun PatchSelectionAccordion(
|
||||
@DrawableRes iconRes: Int,
|
||||
@StringRes titleRes: Int,
|
||||
@StringRes descriptionRes: Int,
|
||||
specs: List<PatchSpec>,
|
||||
enabledCount: Int,
|
||||
totalCount: Int,
|
||||
isEnabled: (KnownPatch) -> Boolean,
|
||||
onToggle: (KnownPatch, Boolean) -> Unit,
|
||||
isEnabled: (PatchSpec) -> Boolean,
|
||||
onToggle: (PatchSpec, Boolean) -> Unit,
|
||||
lockState: (PatchSpec) -> PatchLock,
|
||||
variantIndex: (PatchSpec) -> Int,
|
||||
onSelectVariant: (PatchSpec, Int) -> Unit,
|
||||
optionState: PatchOptionState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -36,6 +58,9 @@ fun PatchSelectionAccordion(
|
||||
label = "patch-accordion-arrow",
|
||||
)
|
||||
|
||||
var lockInfo by remember { mutableStateOf<LockInfo?>(null) }
|
||||
var advancedFor by remember { mutableStateOf<PatchSpec?>(null) }
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
@@ -50,12 +75,17 @@ fun PatchSelectionAccordion(
|
||||
.clickable(role = Role.Button) { expanded = !expanded }
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(iconRes),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patchopts_patches_title),
|
||||
text = stringResource(titleRes),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
@@ -80,34 +110,166 @@ fun PatchSelectionAccordion(
|
||||
|
||||
AnimatedVisibility(visible = expanded) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.background.copy(0.4f))
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.patchopts_patches_desc),
|
||||
text = stringResource(descriptionRes),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier
|
||||
.alpha(.7f)
|
||||
.padding(bottom = 8.dp),
|
||||
)
|
||||
|
||||
for (patch in KnownPatch.All) key(patch) {
|
||||
PatchCheckboxRow(
|
||||
title = stringResource(patch.titleRes),
|
||||
description = stringResource(patch.descRes),
|
||||
checked = isEnabled(patch),
|
||||
onCheckedChange = { onToggle(patch, it) },
|
||||
)
|
||||
for (patch in specs) key(patch.id) {
|
||||
val checked = isEnabled(patch)
|
||||
val lock = lockState(patch)
|
||||
|
||||
val inlineToggles = patch.advancedOptions
|
||||
.filterIsInstance<OptionSpec.Toggle>()
|
||||
.filter { it.inline }
|
||||
val hasSheetOptions = patch.advancedOptions.any { it !is OptionSpec.Toggle || !it.inline }
|
||||
val hasSettings = patch.variants.isNotEmpty() || inlineToggles.isNotEmpty() || hasSheetOptions
|
||||
|
||||
// When a patch is enabled and has settings show a carded view of the settings
|
||||
val carded = checked && hasSettings
|
||||
Column(
|
||||
modifier = if (carded) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.background(MaterialTheme.colorScheme.primary.copy(alpha = 0.07f))
|
||||
.border(
|
||||
width = 1.5.dp,
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.55f),
|
||||
shape = MaterialTheme.shapes.medium,
|
||||
)
|
||||
.padding(horizontal = 12.dp, vertical = 4.dp)
|
||||
} else {
|
||||
Modifier.fillMaxWidth()
|
||||
},
|
||||
) {
|
||||
PatchSwitchRow(
|
||||
title = patch.title,
|
||||
description = patch.description,
|
||||
checked = checked,
|
||||
lock = lock,
|
||||
onCheckedChange = { onToggle(patch, it) },
|
||||
onLockedTap = { lockInfo = LockInfo(patch, lock) },
|
||||
)
|
||||
|
||||
if (hasSettings) {
|
||||
AnimatedVisibility(visible = checked) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 8.dp),
|
||||
) {
|
||||
val resolvedVariant =
|
||||
if (patch.variants.isNotEmpty())
|
||||
patch.resolveVariantIndex(variantIndex(patch)) {
|
||||
optionState.toggle(patch, it)
|
||||
}
|
||||
else -1
|
||||
if (patch.variants.isNotEmpty()) {
|
||||
val effective = patch.effectiveVariants { optionState.toggle(patch, it) }
|
||||
PatchVariantSelector(
|
||||
variants = effective,
|
||||
selectedIndex = effective
|
||||
.indexOfFirst { it.originalIndex == resolvedVariant }
|
||||
.coerceAtLeast(0),
|
||||
onSelect = { pos ->
|
||||
effective.getOrNull(pos)?.let { onSelectVariant(patch, it.originalIndex) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
for (option in inlineToggles) key(option.key) {
|
||||
val show = option.requiresVariant == null ||
|
||||
option.requiresVariant == resolvedVariant
|
||||
if (show) {
|
||||
InlineToggleRow(
|
||||
title = option.title,
|
||||
description = option.description,
|
||||
checked = optionState.toggle(patch, option),
|
||||
onCheckedChange = { optionState.setToggle(patch, option, it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSheetOptions) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
AdvancedOptionsButton(
|
||||
modified = optionState.isModified(patch),
|
||||
onClick = { advancedFor = patch },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lockInfo?.let { info ->
|
||||
PatchLockDialog(
|
||||
thisPatch = info.patch,
|
||||
lock = info.lock,
|
||||
onDismiss = { lockInfo = null },
|
||||
)
|
||||
}
|
||||
|
||||
advancedFor?.let { patch ->
|
||||
PatchAdvancedOptionsSheet(
|
||||
patch = patch,
|
||||
state = optionState,
|
||||
selectedVariant = patch.resolveVariantIndex(variantIndex(patch)) { optionState.toggle(patch, it) },
|
||||
onDismiss = { advancedFor = null },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AdvancedOptionsButton(
|
||||
modified: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
FilledTonalButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_tune),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(stringResource(R.string.patchopts_advanced_button))
|
||||
if (modified) {
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(8.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.primary),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PatchCheckboxRow(
|
||||
private fun InlineToggleRow(
|
||||
title: String,
|
||||
description: String,
|
||||
checked: Boolean,
|
||||
@@ -117,22 +279,62 @@ private fun PatchCheckboxRow(
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
role = Role.Checkbox,
|
||||
role = Role.Switch,
|
||||
) { onCheckedChange(!checked) }
|
||||
.padding(vertical = 4.dp),
|
||||
) {
|
||||
Checkbox(
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(text = title, style = MaterialTheme.typography.titleSmall)
|
||||
Text(
|
||||
text = description,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.alpha(.7f),
|
||||
)
|
||||
}
|
||||
Switch(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PatchSwitchRow(
|
||||
title: String,
|
||||
description: String,
|
||||
checked: Boolean,
|
||||
lock: PatchLock,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
onLockedTap: () -> Unit,
|
||||
) {
|
||||
val interactionSource = remember(::MutableInteractionSource)
|
||||
val isLocked = lock !is PatchLock.Free
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
role = Role.Switch,
|
||||
) {
|
||||
if (isLocked) onLockedTap() else onCheckedChange(!checked)
|
||||
}
|
||||
.alpha(if (isLocked) 0.45f else 1f)
|
||||
.padding(vertical = 6.dp),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.weight(1f),
|
||||
@@ -147,5 +349,82 @@ private fun PatchCheckboxRow(
|
||||
modifier = Modifier.alpha(.7f),
|
||||
)
|
||||
}
|
||||
|
||||
Box {
|
||||
Switch(
|
||||
checked = checked,
|
||||
enabled = !isLocked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
if (isLocked) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.clickable(
|
||||
interactionSource = remember(::MutableInteractionSource),
|
||||
indication = null,
|
||||
role = Role.Switch,
|
||||
) { onLockedTap() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun PatchLockDialog(
|
||||
thisPatch: PatchSpec,
|
||||
lock: PatchLock,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val (titleRes, msgRes, blockerTitle) = when (lock) {
|
||||
is PatchLock.LockedOn -> Triple(
|
||||
R.string.patch_lock_required_title,
|
||||
R.string.patch_lock_required_msg,
|
||||
lock.by.title,
|
||||
)
|
||||
is PatchLock.LockedOff -> Triple(
|
||||
R.string.patch_lock_blocked_title,
|
||||
R.string.patch_lock_blocked_msg,
|
||||
lock.by.title,
|
||||
)
|
||||
PatchLock.RequiresDefaultPackage -> Triple(
|
||||
R.string.patch_lock_pkgname_title,
|
||||
R.string.patch_lock_pkgname_msg,
|
||||
PatchOptions.Default.packageName,
|
||||
)
|
||||
PatchLock.Free -> return
|
||||
}
|
||||
|
||||
BasicAlertDialog(onDismissRequest = onDismiss) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.large,
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
tonalElevation = 6.dp,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 24.dp, end = 12.dp, top = 20.dp, bottom = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(titleRes),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
Text(
|
||||
text = AnnotatedString.fromHtml(stringResource(msgRes, blockerTitle)),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
TextButton(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
) {
|
||||
Text(stringResource(R.string.action_got_it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.EffectiveVariant
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PatchVariantSelector(
|
||||
variants: List<EffectiveVariant>,
|
||||
selectedIndex: Int,
|
||||
onSelect: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SingleChoiceSegmentedButtonRow(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
variants.forEachIndexed { index, variant ->
|
||||
SegmentedButton(
|
||||
selected = index == selectedIndex,
|
||||
enabled = variant.enabled,
|
||||
onClick = { if (variant.enabled) onSelect(index) },
|
||||
shape = SegmentedButtonDefaults.itemShape(
|
||||
index = index,
|
||||
count = variants.size,
|
||||
),
|
||||
icon = {},
|
||||
label = {
|
||||
Text(
|
||||
text = variant.title,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
-4
@@ -3,8 +3,12 @@ package com.meowarex.rlmobile.ui.screens.patchopts.components.options
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.components.Label
|
||||
import com.meowarex.rlmobile.ui.components.ResetToDefaultButton
|
||||
|
||||
@@ -18,6 +22,8 @@ fun TextPatchOption(
|
||||
onValueChange: (String) -> Unit,
|
||||
onValueReset: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
locked: Boolean? = null,
|
||||
onLockClick: () -> Unit = {},
|
||||
extra: (@Composable ColumnScope.() -> Unit)? = null,
|
||||
) {
|
||||
Label(
|
||||
@@ -30,16 +36,41 @@ fun TextPatchOption(
|
||||
onValueChange = onValueChange,
|
||||
isError = valueIsError,
|
||||
singleLine = true,
|
||||
enabled = locked != true,
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||
errorContainerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||
disabledContainerColor = MaterialTheme.colorScheme.surfaceContainerLow,
|
||||
),
|
||||
trailingIcon = {
|
||||
ResetToDefaultButton(
|
||||
enabled = !valueIsDefault,
|
||||
onClick = onValueReset,
|
||||
)
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (locked != true) {
|
||||
ResetToDefaultButton(
|
||||
enabled = !valueIsDefault,
|
||||
onClick = onValueReset,
|
||||
)
|
||||
}
|
||||
if (locked != null) {
|
||||
IconButton(
|
||||
onClick = onLockClick,
|
||||
modifier = Modifier.size(32.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
if (locked) R.drawable.ic_lock else R.drawable.ic_lock_open,
|
||||
),
|
||||
contentDescription = stringResource(
|
||||
if (locked) R.string.patchopts_field_unlock else R.string.patchopts_field_lock,
|
||||
),
|
||||
tint = if (locked) MaterialTheme.colorScheme.primary
|
||||
else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(8.dp))
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
+4
@@ -55,6 +55,10 @@ class UpdaterViewModel(
|
||||
showDialog = false
|
||||
}
|
||||
|
||||
fun reopenDialog() {
|
||||
if (targetVersion != null) showDialog = true
|
||||
}
|
||||
|
||||
fun triggerUpdate() = viewModelScope.launchIO {
|
||||
if (!isWorking.compareAndSet(expect = false, update = true))
|
||||
return@launchIO
|
||||
|
||||
+9
-5
@@ -7,7 +7,6 @@ import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.app.ActivityCompat
|
||||
@@ -51,15 +50,15 @@ class UpdateCheckWorker(
|
||||
.maxByOrNull { it.first }
|
||||
?: return Result.success()
|
||||
|
||||
val (version, release) = latestVersion
|
||||
val (version, _) = latestVersion
|
||||
if (current >= version) return Result.success()
|
||||
|
||||
Log.i(BuildConfig.TAG, "Update available: $version (installed $current)")
|
||||
postUpdateNotification(version.toString(), release.htmlUrl)
|
||||
postUpdateNotification(version.toString())
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private fun postUpdateNotification(version: String, releaseUrl: String) {
|
||||
private fun postUpdateNotification(version: String) {
|
||||
val nm = applicationContext.getSystemService<NotificationManager>() ?: return
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -78,10 +77,15 @@ class UpdateCheckWorker(
|
||||
return
|
||||
}
|
||||
|
||||
val launchIntent = applicationContext.packageManager
|
||||
.getLaunchIntentForPackage(applicationContext.packageName)
|
||||
?.apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }
|
||||
?: return
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
applicationContext,
|
||||
0,
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(releaseUrl)).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) },
|
||||
launchIntent,
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
)
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import android.app.Activity
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
import android.provider.Settings
|
||||
@@ -63,6 +65,15 @@ fun Context.getPackageVersion(pkg: String): Pair<String?, Int> {
|
||||
.let { it.versionName to it.versionCode }
|
||||
}
|
||||
|
||||
// Whether the device currently has a validated internet connection
|
||||
fun Context.isOnline(): Boolean {
|
||||
val cm = getSystemService<ConnectivityManager>() ?: return true
|
||||
val network = cm.activeNetwork ?: return false
|
||||
val caps = cm.getNetworkCapabilities(network) ?: return false
|
||||
return caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
|
||||
caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||
}
|
||||
|
||||
fun Context.isPackageInstalled(packageName: String): Boolean {
|
||||
return try {
|
||||
packageManager.getPackageInfo(packageName, 0)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M218,856L104,742Q81,719 81,686Q81,653 104,630L630,104Q653,81 686,81Q719,81 742,104L856,218Q879,241 879,274Q879,307 856,330L330,856Q307,879 274,879Q241,879 218,856ZM508,734L734,508L856,630Q879,653 879,686Q879,719 856,742L742,856Q719,879 686,879Q653,879 630,856L508,734ZM480,600Q497,600 508.5,588.5Q520,577 520,560Q520,543 508.5,531.5Q497,520 480,520Q463,520 451.5,531.5Q440,543 440,560Q440,577 451.5,588.5Q463,600 480,600ZM400,520Q417,520 428.5,508.5Q440,497 440,480Q440,463 428.5,451.5Q417,440 400,440Q383,440 371.5,451.5Q360,463 360,480Q360,497 371.5,508.5Q383,520 400,520ZM560,520Q577,520 588.5,508.5Q600,497 600,480Q600,463 588.5,451.5Q577,440 560,440Q543,440 531.5,451.5Q520,463 520,480Q520,497 531.5,508.5Q543,520 560,520ZM225,451L104,330Q81,307 81,274Q81,241 104,218L218,104Q241,81 274,81Q307,81 330,104L452,226L225,451ZM480,440Q497,440 508.5,428.5Q520,417 520,400Q520,383 508.5,371.5Q497,360 480,360Q463,360 451.5,371.5Q440,383 440,400Q440,417 451.5,428.5Q463,440 480,440Z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8H8.9V6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1s3.1,1.39 3.1,3.1v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10c0,-1.1 -0.9,-2 -2,-2zM18,20H6V10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z" />
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M120,880v-800l60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60v800l-60,-60 -60,60 -60,-60 -60,60 -60,-60 -60,60 -60,-60 -60,60 -60,-60 -60,60 -60,-60 -60,60ZM240,680h480v-80L240,600v80ZM240,520h480v-80L240,440v80ZM240,360h480v-80L240,280v80Z" />
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="m819,932 l-59,-59q-10,3 -19.5,5T720,880L240,880q-50,0 -85,-35t-35,-85v-120h120v-287L27,140l57,-57L876,875l-57,57ZM840,727 L760,647v-447L313,200l-73,-73v-47l60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60v647ZM320,640h207L320,433v207ZM475,360 L395,280h205v80L475,360ZM595,480 L515,400h85v80h-5ZM680,480q-17,0 -28.5,-11.5T640,440q0,-17 11.5,-28.5T680,400q17,0 28.5,11.5T720,440q0,17 -11.5,28.5T680,480ZM680,360q-17,0 -28.5,-11.5T640,320q0,-17 11.5,-28.5T680,280q17,0 28.5,11.5T720,320q0,17 -11.5,28.5T680,360Z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M3,17v2h6v-2H3zM3,5v2h10V5H3zM13,21v-2h8v-2h-8v-2h-2v6h2zM7,9v2H3v2h4v2h2V9H7zM21,13v-2H11v2h10zM15,7h2V5h4V3h-4V1h-2v6z" />
|
||||
</vector>
|
||||
@@ -5,5 +5,5 @@
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M480,840q-75,0 -140.5,-28.5t-114,-77q-48.5,-48.5 -77,-114T120,480q0,-75 28.5,-140.5t77,-114q48.5,-48.5 114,-77T480,120q82,0 155.5,35T760,254v-54q0,-17 11.5,-28.5T800,160q17,0 28.5,11.5T840,200v160q0,17 -11.5,28.5T800,400L640,400q-17,0 -28.5,-11.5T600,360q0,-17 11.5,-28.5T640,320h70q-41,-56 -101,-88t-129,-32q-117,0 -198.5,81.5T200,480q0,117 81.5,198.5T480,760q95,0 170,-57t99,-147q5,-16 18,-24t29,-6q17,2 27,14.5t6,27.5q-29,119 -126,195.5T480,840ZM520,464 L620,564q11,11 11,28t-11,28q-11,11 -28,11t-28,-11L452,508q-6,-6 -9,-13.5t-3,-15.5v-159q0,-17 11.5,-28.5T480,280q17,0 28.5,11.5T520,320v144Z" />
|
||||
android:pathData="M480,640q17,0 28.5,-11.5T520,600v-184l64,64q11,11 28,11t28,-11q11,-11 11,-28t-11,-28L508,308q-12,-12 -28,-12t-28,12L324,424q-11,11 -11,28t11,28q11,11 28,11t28,-11l64,-64v184q0,17 11.5,28.5T480,640Zm0,240q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880Z" />
|
||||
</vector>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<string name="action_retry_install">Retry installation</string>
|
||||
<string name="action_open_error_log">Open error log</string>
|
||||
<string name="action_apply">Apply</string>
|
||||
<string name="action_done">Done</string>
|
||||
<string name="action_confirm">Confirm</string>
|
||||
<string name="action_dismiss">Dismiss</string>
|
||||
<string name="action_install">Install</string>
|
||||
@@ -129,7 +130,6 @@
|
||||
|
||||
<string name="navigation_back">Back</string>
|
||||
<string name="navigation_about">About</string>
|
||||
<string name="navigation_logs">Logs</string>
|
||||
<string name="navigation_settings">Settings</string>
|
||||
<string name="navigation_refresh">Refresh</string>
|
||||
|
||||
@@ -180,7 +180,7 @@
|
||||
<string name="install_error_timeout">Installer timed out</string>
|
||||
|
||||
<string name="install_group_prepare">Prepare</string>
|
||||
<string name="install_group_download">Download dependencies</string>
|
||||
<string name="install_group_download">Download Batteries</string>
|
||||
<string name="install_group_patch">Patch APK</string>
|
||||
<string name="install_group_install">Install APK</string>
|
||||
|
||||
@@ -247,11 +247,23 @@
|
||||
<string name="patchopts_custom_tidal_apk_desc">Provide your own Stock TIDAL APK to patch instead of the version from Github.</string>
|
||||
<string name="patchopts_custom_patches_title">Custom Patches</string>
|
||||
<string name="patchopts_custom_patches_desc">Provide your own Patches Zip to use instead of the ones from Github.</string>
|
||||
<string name="patchopts_bypass_incompatible_title">Bypass Incompatible</string>
|
||||
<string name="patchopts_bypass_incompatible_desc">Force-allow path-gated patches. This can be VERY DANGEROUS!</string>
|
||||
<string name="patchopts_divider_basic">Basic</string>
|
||||
<string name="patchopts_divider_advanced">Advanced</string>
|
||||
<string name="patchopts_patches_title">Patches</string>
|
||||
<string name="patchopts_patches_desc">Toggle which patches are applied during installation. Unchecked patches are skipped entirely.</string>
|
||||
<string name="patchopts_patches_summary">%1$d of %2$d enabled</string>
|
||||
<string name="patchopts_integrations_title">Integrations</string>
|
||||
<string name="patchopts_integrations_desc">Connect TIDAL to other systems/apps</string>
|
||||
<string name="patchopts_pkgname_warning_title">Custom Package Name</string>
|
||||
<string name="patchopts_pkgname_warning_msg">Installing under a package name other than <b>%s</b> will block the following from working:</string>
|
||||
<string name="patchopts_pkgname_warning_permanent">The package name is permanent once installed (requires a fresh install)</string>
|
||||
<string name="patchopts_advanced_button">Advanced Options</string>
|
||||
<string name="patchopts_advanced_sheet_label">Advanced Options</string>
|
||||
<string name="patchopts_toggle_snap">Toggle snapping to steps</string>
|
||||
<string name="patchopts_field_unlock">Unlock editing</string>
|
||||
<string name="patchopts_field_lock">Lock and reset to default</string>
|
||||
|
||||
<string name="patch_lyrics_disable_cover_title">Disable Lyrics Cover</string>
|
||||
<string name="patch_lyrics_disable_cover_desc">Removes the mini track cover from the lyrics screen.</string>
|
||||
@@ -259,8 +271,22 @@
|
||||
<string name="patch_lyrics_progress_pill_desc">Restores the old Track Progress Pill in the top of the Lyrics screen!</string>
|
||||
<string name="patch_lyrics_replace_button_title">Replace Lyrics Button</string>
|
||||
<string name="patch_lyrics_replace_button_desc">Replaces the Lyrics button with the RL Sparkle!</string>
|
||||
<string name="patch_lyrics_rl_api_title">Radiant Lyrics API - WIP</string>
|
||||
<string name="patch_lyrics_rl_api_desc">Use Radiant Lyrics API to fetch Lyrics (Higher quality & More providers)</string>
|
||||
<string name="patch_sticky_lyrics_title">Sticky Lyrics</string>
|
||||
<string name="patch_sticky_lyrics_desc">Always Forces the Lyrics page to be opened (aslong as the track has lyrics)</string>
|
||||
<string name="patch_lyrics_keep_controls_title">Keep Controls Visible</string>
|
||||
<string
|
||||
name="patch_lyrics_keep_controls_desc"
|
||||
>Inverts the auto-hide behavior on the lyrics screen, playback controls stay visible by default and only hide when you tap them off.</string>
|
||||
<string name="patch_player_backdrop_title">Player Backdrop</string>
|
||||
<string name="patch_player_backdrop_desc">Restores the legacy translucent backdrop blur behind the player.</string>
|
||||
<string name="patch_quality_badge_colors_title">Quality Badge Colors</string>
|
||||
<string name="patch_quality_badge_colors_desc">Brings color back to the player quality badge.</string>
|
||||
<string name="patch_player_one_handed_title">One-Handed Mode</string>
|
||||
<string name="patch_player_one_handed_desc">Mirrors the bottom player buttons left-to-right for easier one-handed reach.</string>
|
||||
<string name="patch_cover_everywhere_title">Cover Everywhere - WIP</string>
|
||||
<string name="patch_cover_everywhere_desc">Applies the blurred backdrop to every Compose based page (Home & Collection)</string>
|
||||
<string name="patch_lyrics_replace_share_button_title">Replace Share Button</string>
|
||||
<string
|
||||
name="patch_lyrics_replace_share_button_desc"
|
||||
@@ -269,11 +295,60 @@
|
||||
<string
|
||||
name="patch_debug_menu_unlock_desc"
|
||||
>Reveals TIDAL\'s hidden Debug Menu in Settings. (Unlocks Feature Flags & the legacy Debug Options)</string>
|
||||
<string name="patch_waze_integration_title">Waze Integration</string>
|
||||
<string name="patch_waze_integration_desc">Control TIDAL playback from the Waze app</string>
|
||||
<string name="patch_waze_browse_root_title">Media Menu</string>
|
||||
<string name="patch_waze_accent_color_title">Accent Color</string>
|
||||
<string name="patch_waze_color_waze_default">Waze Default</string>
|
||||
<string name="patch_waze_color_tidal_cyan">TIDAL Cyan</string>
|
||||
<string name="patch_waze_color_material_you">Material You</string>
|
||||
<string name="patch_waze_root_auto">Auto Menu</string>
|
||||
<string name="patch_waze_root_home">Home</string>
|
||||
<string name="patch_waze_root_recents">Recents</string>
|
||||
<string name="patch_color_picker_title">Custom color</string>
|
||||
<string name="patch_color_picker_hex">Hex color</string>
|
||||
<string name="patch_color_picker_hex_error">Use #RRGGBB or #AARRGGBB.</string>
|
||||
<string name="patch_enable_legacy_ui_title">Enable Legacy UI</string>
|
||||
<string
|
||||
name="patch_enable_legacy_ui_desc"
|
||||
>[This patch will stop working soon] - Replaces the New Compose based UI with the Legacy UI (disables player-market-ui feature flag)</string>
|
||||
|
||||
<string name="patch_mini_player_redesign_title">Redesigned Mini-Player</string>
|
||||
<string name="patch_mini_player_redesign_desc">Integrates the Seekbar into the control panel at the bottom of the screen.</string>
|
||||
<string name="patch_mini_player_gestures_title">Mini-Player Gestures</string>
|
||||
<string name="patch_mini_player_gestures_desc">Swipe up on the mini-player to open the full player.</string>
|
||||
<string name="patch_mini_player_drag_title">Swipe Up Style</string>
|
||||
<string name="patch_mini_player_drag_desc">Whether the player follows your finger when swiping up to open it.</string>
|
||||
<string name="patch_mini_player_drag_static">Static</string>
|
||||
<string name="patch_mini_player_drag_drag">Drag</string>
|
||||
<string name="patch_mini_player_left_right_gestures_title">Next/Previous Gestures</string>
|
||||
<string name="patch_mini_player_left_right_gestures_desc">Swipe left or right on the mini-player to skip tracks.</string>
|
||||
<string name="patch_mini_player_dynamic_bg_title">Mini-Player Dynamic Background</string>
|
||||
<string name="patch_mini_player_dynamic_bg_desc">Colorizes the Mini-Player depending on the currently playing track.</string>
|
||||
<string name="patch_mini_player_variant_floating_title">Floating</string>
|
||||
<string name="patch_mini_player_variant_square_grey_title">Grey</string>
|
||||
<string name="patch_mini_player_variant_square_black_title">Black</string>
|
||||
<string name="patch_mini_player_variant_legacy_title">Legacy</string>
|
||||
<string name="patch_mini_player_border_title">Progress Border</string>
|
||||
<string name="patch_mini_player_border_desc">Draws an animated progress ring around the floating mini-player. Requires the Floating style.</string>
|
||||
|
||||
<!-- Advanced option labels -->
|
||||
<string name="patch_opt_backdrop_blur_title">Blur Strength</string>
|
||||
<string name="patch_opt_backdrop_blur_desc">How strongly the album art behind the player is blurred.</string>
|
||||
<string name="patch_opt_backdrop_dimming_title">Backdrop Dimming</string>
|
||||
<string name="patch_opt_backdrop_dimming_desc">Adjusts the level of darkening on the backdrop to keep foreground controls legible.</string>
|
||||
|
||||
<string name="patch_lock_required_title">Patch Required!</string>
|
||||
<string name="patch_lock_blocked_title">Patch Blocked!</string>
|
||||
<string name="patch_lock_required_msg">Patch Required by <b>%s</b>.</string>
|
||||
<string name="patch_lock_blocked_msg">Patch Blocked by <b>%s</b>.</string>
|
||||
<string name="patch_lock_pkgname_title">Original Package Name Invalid!</string>
|
||||
<string name="patch_lock_pkgname_msg">This only works while the app keeps its original <b>%s</b> package name. Reset the Package Name option to enable it.<br/><br/>An existing install can\'t be renamed (requires a fresh install)</string>
|
||||
<string name="patch_opt_lock_title">Option Locked</string>
|
||||
<string name="patch_opt_lock_needs_variant">Requires the <b>%s</b> style.</string>
|
||||
<string name="patch_opt_lock_needs_option">Requires the <b>%s</b> option.</string>
|
||||
<string name="action_got_it">Got it</string>
|
||||
|
||||
<string name="componentopts_screen_title">Custom Component (%s)</string>
|
||||
<string name="componentopts_screen_desc">Select a custom build that was imported by Manager.</string>
|
||||
<string name="componentopts_selected_none">Latest</string>
|
||||
@@ -293,13 +368,6 @@
|
||||
<string name="log_action_export_apk">Export APK</string>
|
||||
<string name="log_action_share">Share log</string>
|
||||
|
||||
<string name="logs_title">Logs</string>
|
||||
<string name="logs_none">No logs present!</string>
|
||||
<string name="logs_action_delete_all">Delete all logs</string>
|
||||
<string name="logs_status_delete_success">Successfully deleted all logs</string>
|
||||
<string name="logs_wipe_title">Clear Logs</string>
|
||||
<string name="logs_wipe_desc">Are you sure you want to permanently delete all the logs?</string>
|
||||
|
||||
<string name="notif_group_install_title">Active installation</string>
|
||||
<string name="notif_group_install_desc">Progress notifications sent during a minimized installation</string>
|
||||
<string name="notif_install_ready_title">Radiant Lyrics installation ready!</string>
|
||||
|
||||
@@ -2,13 +2,25 @@
|
||||
|
||||
A Manager app for Android that patches TIDAL to bring the Radiant Lyrics experience to mobile.
|
||||
|
||||
## What it does
|
||||
## What it does?
|
||||
|
||||
- Blurred album art background in the player
|
||||
- Progress pill overlay in lyrics mode - restored from old UI
|
||||
- Hides the ugly cover art when in lyrics mode
|
||||
- Syllable Level lyrics (SoonTM)
|
||||
This project adds ALLOT of completely Optional & Customisable Patches/Integrations to TIDAL on Android (iOS SoonTM).
|
||||
Every Patch & Integration can be disabled/enabled & adjusted exactly how you want TIDAL to feel/look <3
|
||||
|
||||
### Patches:
|
||||
|
||||
- Insane Customisation of the Player Backdrop
|
||||
- Higher quality lyrics from more providers (WIP)
|
||||
- Progress pill overlay in lyrics mode (restored from old UI)
|
||||
- Keeps lyrics controls visible
|
||||
- One-handed player controls
|
||||
- Redesigned mini-player with gestures
|
||||
- Word Level Lyrics (Word by Word) <3
|
||||
- Syllable Level Lyrics (Syllable by Syllable) <3
|
||||
|
||||
### Integrations
|
||||
|
||||
- Waze | Control TIDAL playback from Waze
|
||||
|
||||
### Disclaimer
|
||||
|
||||
@@ -18,7 +30,10 @@ This project is not affiliated with TIDAL in any way. Use at your own risk.
|
||||
## Installation
|
||||
|
||||
### Batteries <3
|
||||
- Android 10.0+ & >340 DPI
|
||||
- Phone of some unknown description
|
||||
- The smallest amount of brain damage
|
||||
- Android 7.0+ & >340 DPI
|
||||
- Internet
|
||||
- Paid TIDAL Account
|
||||
|
||||
### Steps
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/com/tidal/wave2/foundation/WaveScaffoldKt.smali
|
||||
+++ b/com/tidal/wave2/foundation/WaveScaffoldKt.smali
|
||||
@@ -889,6 +889,10 @@
|
||||
.line 386
|
||||
.line 387
|
||||
:cond_20
|
||||
+ const/4 v0, 0x0 # render $changed flag
|
||||
+
|
||||
+ invoke-static {v1, v0}, Lradiant/HomeBackdrop;->render(Landroidx/compose/runtime/Composer;I)V # render album-art backdrop
|
||||
+
|
||||
new-instance v0, Lcom/tidal/wave2/foundation/WaveScaffoldKt$WaveScaffold$2;
|
||||
|
||||
.line 388
|
||||
@@ -991,7 +995,7 @@
|
||||
|
||||
.line 436
|
||||
.line 437
|
||||
- move-wide v15, v10
|
||||
+ const-wide/16 v15, 0x0 # transparent containerColor
|
||||
|
||||
.line 438
|
||||
const/4 v11, 0x0
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/com/tidal/android/feature/appscaffold/ui/q.smali
|
||||
+++ b/com/tidal/android/feature/appscaffold/ui/q.smali
|
||||
@@ -55,6 +55,8 @@
|
||||
.line 10
|
||||
iput-object p4, p0, Lcom/tidal/android/feature/appscaffold/ui/q;->d:Ljava/lang/String;
|
||||
|
||||
+ invoke-static {p3, p4}, Lradiant/HomeBackdrop;->fromMiniPlayer(ILjava/lang/String;)V # capture cover state
|
||||
+
|
||||
.line 11
|
||||
.line 12
|
||||
iput-boolean p5, p0, Lcom/tidal/android/feature/appscaffold/ui/q;->e:Z
|
||||
--- a/com/tidal/android/feature/appscaffold/ui/t.smali
|
||||
+++ b/com/tidal/android/feature/appscaffold/ui/t.smali
|
||||
@@ -23,6 +23,8 @@
|
||||
.method public constructor <init>(Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$ItemType;Lcom/tidal/android/feature/appscaffold/ui/q;Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$PlayState;Z)V
|
||||
.locals 0
|
||||
|
||||
+ invoke-static {p3}, Lradiant/HomeBackdrop;->onPlayState(Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$PlayState;)V # RL: publish live play state to Cover Everywhere
|
||||
+
|
||||
.line 1
|
||||
invoke-virtual {p1}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"tidalVersionCode": 9090,
|
||||
"tidalVersionCode": 10004,
|
||||
"tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk",
|
||||
"patchesVersion": "0.7.6"
|
||||
"patchesVersion": "0.12.0"
|
||||
}
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
@@ -54,6 +54,9 @@
|
||||
.method public final a()Z
|
||||
.locals 2
|
||||
|
||||
|
||||
+ const/4 v0, 0x1 # force true
|
||||
+ return v0 # bypass debug flag
|
||||
+
|
||||
.line 1
|
||||
iget-object v0, p0, Lcom/tidal/android/core/debug/DebugFeatureInteractorDefault;->a:Lcom/tidal/android/featureflags/l;
|
||||
|
||||
@@ -98,6 +101,14 @@
|
||||
iget-object v0, p0, Lcom/tidal/android/core/debug/DebugFeatureInteractorDefault;->a:Lcom/tidal/android/featureflags/n;
|
||||
|
||||
@@ -99,6 +102,14 @@
|
||||
}
|
||||
.end annotation
|
||||
|
||||
|
||||
+ sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean; # always-true value
|
||||
+
|
||||
+ invoke-static {v0}, Lkotlinx/coroutines/flow/StateFlowKt;->MutableStateFlow(Ljava/lang/Object;)Lkotlinx/coroutines/flow/MutableStateFlow; # wrap in flow
|
||||
@@ -23,14 +23,15 @@
|
||||
+ return-object v0 # short-circuit b()
|
||||
+
|
||||
.line 1
|
||||
sget-object v0, Lj50/a;->a:Ljava/lang/String;
|
||||
|
||||
@@ -261,5 +272,8 @@
|
||||
sget-object v0, Lo50/a;->a:Ljava/lang/String;
|
||||
|
||||
@@ -261,6 +272,9 @@
|
||||
.method public final c()Z
|
||||
.locals 3
|
||||
|
||||
|
||||
+ const/4 v0, 0x1 # force true
|
||||
+ return v0 # bypass debug flag
|
||||
+
|
||||
.line 1
|
||||
sget-object v0, Lj50/a;->a:Ljava/lang/String;
|
||||
sget-object v0, Lo50/a;->a:Ljava/lang/String;
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
--- a/kg/e.smali
|
||||
+++ b/kg/e.smali
|
||||
@@ -26,8 +26,8 @@
|
||||
new-instance v0, Lkg/e;
|
||||
|
||||
--- a/hg/d.smali
|
||||
+++ b/hg/d.smali
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
.line 2
|
||||
.line 3
|
||||
- const-string v1, "player-market-ui"
|
||||
+ const-string v1, "player-market-ui-rl-forced-off" # break flag key lookup
|
||||
|
||||
|
||||
.line 4
|
||||
.line 5
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
.class public final Lcom/tidal/android/feature/appscaffold/ui/r$c;
|
||||
.super Lcom/tidal/android/feature/appscaffold/ui/r;
|
||||
.source "SourceFile"
|
||||
|
||||
# Synthetic mini-player event used by right swipe (previous/rewind).
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lcom/tidal/android/feature/appscaffold/ui/r;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "c"
|
||||
.end annotation
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static final a:Lcom/tidal/android/feature/appscaffold/ui/r$c;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
new-instance v0, Lcom/tidal/android/feature/appscaffold/ui/r$c;
|
||||
|
||||
invoke-direct {v0}, Lcom/tidal/android/feature/appscaffold/ui/r;-><init>()V
|
||||
|
||||
sput-object v0, Lcom/tidal/android/feature/appscaffold/ui/r$c;->a:Lcom/tidal/android/feature/appscaffold/ui/r$c;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final equals(Ljava/lang/Object;)Z
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x1
|
||||
|
||||
if-ne p0, p1, :cond_0
|
||||
|
||||
return v0
|
||||
|
||||
:cond_0
|
||||
instance-of p1, p1, Lcom/tidal/android/feature/appscaffold/ui/r$c;
|
||||
|
||||
if-nez p1, :cond_1
|
||||
|
||||
const/4 p1, 0x0
|
||||
|
||||
return p1
|
||||
|
||||
:cond_1
|
||||
return v0
|
||||
.end method
|
||||
|
||||
.method public final hashCode()I
|
||||
.locals 1
|
||||
|
||||
const v0, -0x5d6a0f8
|
||||
|
||||
return v0
|
||||
.end method
|
||||
|
||||
.method public final toString()Ljava/lang/String;
|
||||
.locals 1
|
||||
|
||||
const-string v0, "PreviousButtonClicked"
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
@@ -0,0 +1,133 @@
|
||||
.class public final Lradiant/BackdropFx;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
.method public static apply(Landroid/graphics/Bitmap;)Landroid/graphics/Bitmap;
|
||||
.locals 14
|
||||
|
||||
const v0, __RL_CONTRAST__
|
||||
|
||||
const v1, __RL_SATURATION__
|
||||
|
||||
const v2, __RL_BRIGHTNESS__
|
||||
|
||||
# cm = ColorMatrix(); cm.setSaturation(s)
|
||||
new-instance v3, Landroid/graphics/ColorMatrix;
|
||||
|
||||
invoke-direct {v3}, Landroid/graphics/ColorMatrix;-><init>()V
|
||||
|
||||
invoke-virtual {v3, v1}, Landroid/graphics/ColorMatrix;->setSaturation(F)V
|
||||
|
||||
# o = 128 - 128*c (contrast pivot at mid-grey, 0..255 space)
|
||||
const/high16 v4, 0x43000000
|
||||
|
||||
mul-float v5, v4, v0
|
||||
|
||||
sub-float v5, v4, v5
|
||||
|
||||
# contrast matrix: diag = c, offset column = o
|
||||
const/16 v6, 0x14
|
||||
|
||||
new-array v7, v6, [F
|
||||
|
||||
const/4 v8, 0x0
|
||||
|
||||
aput v0, v7, v8
|
||||
|
||||
const/4 v8, 0x4
|
||||
|
||||
aput v5, v7, v8
|
||||
|
||||
const/4 v8, 0x6
|
||||
|
||||
aput v0, v7, v8
|
||||
|
||||
const/16 v8, 0x9
|
||||
|
||||
aput v5, v7, v8
|
||||
|
||||
const/16 v8, 0xc
|
||||
|
||||
aput v0, v7, v8
|
||||
|
||||
const/16 v8, 0xe
|
||||
|
||||
aput v5, v7, v8
|
||||
|
||||
const/high16 v9, 0x3f800000
|
||||
|
||||
const/16 v8, 0x12
|
||||
|
||||
aput v9, v7, v8
|
||||
|
||||
new-instance v8, Landroid/graphics/ColorMatrix;
|
||||
|
||||
invoke-direct {v8, v7}, Landroid/graphics/ColorMatrix;-><init>([F)V
|
||||
|
||||
invoke-virtual {v3, v8}, Landroid/graphics/ColorMatrix;->postConcat(Landroid/graphics/ColorMatrix;)V
|
||||
|
||||
# brightness matrix: diag = b
|
||||
new-array v7, v6, [F
|
||||
|
||||
const/4 v8, 0x0
|
||||
|
||||
aput v2, v7, v8
|
||||
|
||||
const/4 v8, 0x6
|
||||
|
||||
aput v2, v7, v8
|
||||
|
||||
const/16 v8, 0xc
|
||||
|
||||
aput v2, v7, v8
|
||||
|
||||
const/16 v8, 0x12
|
||||
|
||||
aput v9, v7, v8
|
||||
|
||||
new-instance v8, Landroid/graphics/ColorMatrix;
|
||||
|
||||
invoke-direct {v8, v7}, Landroid/graphics/ColorMatrix;-><init>([F)V
|
||||
|
||||
invoke-virtual {v3, v8}, Landroid/graphics/ColorMatrix;->postConcat(Landroid/graphics/ColorMatrix;)V
|
||||
|
||||
# out = createBitmap(w, h, ARGB_8888)
|
||||
invoke-virtual {p0}, Landroid/graphics/Bitmap;->getWidth()I
|
||||
|
||||
move-result v10
|
||||
|
||||
invoke-virtual {p0}, Landroid/graphics/Bitmap;->getHeight()I
|
||||
|
||||
move-result v11
|
||||
|
||||
sget-object v12, Landroid/graphics/Bitmap$Config;->ARGB_8888:Landroid/graphics/Bitmap$Config;
|
||||
|
||||
invoke-static {v10, v11, v12}, Landroid/graphics/Bitmap;->createBitmap(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;
|
||||
|
||||
move-result-object v10
|
||||
|
||||
# canvas over out
|
||||
new-instance v11, Landroid/graphics/Canvas;
|
||||
|
||||
invoke-direct {v11, v10}, Landroid/graphics/Canvas;-><init>(Landroid/graphics/Bitmap;)V
|
||||
|
||||
# paint with the color-matrix filter
|
||||
new-instance v12, Landroid/graphics/Paint;
|
||||
|
||||
invoke-direct {v12}, Landroid/graphics/Paint;-><init>()V
|
||||
|
||||
new-instance v13, Landroid/graphics/ColorMatrixColorFilter;
|
||||
|
||||
invoke-direct {v13, v3}, Landroid/graphics/ColorMatrixColorFilter;-><init>(Landroid/graphics/ColorMatrix;)V
|
||||
|
||||
invoke-virtual {v12, v13}, Landroid/graphics/Paint;->setColorFilter(Landroid/graphics/ColorFilter;)Landroid/graphics/ColorFilter;
|
||||
|
||||
# draw the blurred bitmap through the filter
|
||||
const/4 v0, 0x0
|
||||
|
||||
invoke-virtual {v11, p0, v0, v0, v12}, Landroid/graphics/Canvas;->drawBitmap(Landroid/graphics/Bitmap;FFLandroid/graphics/Paint;)V
|
||||
|
||||
invoke-virtual {p0}, Landroid/graphics/Bitmap;->recycle()V
|
||||
|
||||
return-object v10
|
||||
.end method
|
||||
@@ -0,0 +1,415 @@
|
||||
.class public final Lradiant/HomeBackdrop;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static volatile currentAlbumId:I
|
||||
|
||||
# 0 = Static, 1 = Animated (always rotate), 2 = Playback (follow live play state)
|
||||
.field public static volatile mode:I
|
||||
|
||||
# Live play state from the mini-player (t.c PlayState = Playing)
|
||||
.field public static isPlayingState:Landroidx/compose/runtime/MutableState;
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"Landroidx/compose/runtime/MutableState<",
|
||||
"Ljava/lang/Boolean;",
|
||||
">;"
|
||||
}
|
||||
.end annotation
|
||||
.end field
|
||||
|
||||
.field public static coverUuidState:Landroidx/compose/runtime/MutableState;
|
||||
.annotation system Ldalvik/annotation/Signature;
|
||||
value = {
|
||||
"Landroidx/compose/runtime/MutableState<",
|
||||
"Ljava/lang/String;",
|
||||
">;"
|
||||
}
|
||||
.end annotation
|
||||
.end field
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 4
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput v0, Lradiant/HomeBackdrop;->currentAlbumId:I
|
||||
|
||||
const/4 v0, 0x2
|
||||
|
||||
sput v0, Lradiant/HomeBackdrop;->mode:I
|
||||
|
||||
sget-object v0, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;
|
||||
|
||||
const/4 v1, 0x0
|
||||
|
||||
const/4 v2, 0x2
|
||||
|
||||
const/4 v3, 0x0
|
||||
|
||||
invoke-static {v0, v1, v2, v3}, Landroidx/compose/runtime/SnapshotStateKt;->mutableStateOf$default(Ljava/lang/Object;Landroidx/compose/runtime/SnapshotMutationPolicy;ILjava/lang/Object;)Landroidx/compose/runtime/MutableState;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
sput-object v0, Lradiant/HomeBackdrop;->isPlayingState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
const/4 v1, 0x0
|
||||
|
||||
const/4 v2, 0x2
|
||||
|
||||
const/4 v3, 0x0
|
||||
|
||||
invoke-static {v0, v1, v2, v3}, Landroidx/compose/runtime/SnapshotStateKt;->mutableStateOf$default(Ljava/lang/Object;Landroidx/compose/runtime/SnapshotMutationPolicy;ILjava/lang/Object;)Landroidx/compose/runtime/MutableState;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
sput-object v0, Lradiant/HomeBackdrop;->coverUuidState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static setModeStatic()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput v0, Lradiant/HomeBackdrop;->mode:I
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static setModeAnimated()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x1
|
||||
|
||||
sput v0, Lradiant/HomeBackdrop;->mode:I
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static setModePlayback()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x2
|
||||
|
||||
sput v0, Lradiant/HomeBackdrop;->mode:I
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
# from the mini-player state ctor (t.<init>) whenever play state changes.
|
||||
.method public static onPlayState(Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$PlayState;)V
|
||||
.locals 2
|
||||
|
||||
sget-object v0, Lradiant/HomeBackdrop;->isPlayingState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
if-nez v0, :cond_ready
|
||||
|
||||
return-void
|
||||
|
||||
:cond_ready
|
||||
sget-object v1, Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$PlayState;->Playing:Lcom/tidal/android/feature/appscaffold/ui/MiniPlayerContract$PlayState;
|
||||
|
||||
if-ne p0, v1, :cond_paused
|
||||
|
||||
sget-object v1, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;
|
||||
|
||||
goto :goto_set
|
||||
|
||||
:cond_paused
|
||||
sget-object v1, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;
|
||||
|
||||
:goto_set
|
||||
invoke-interface {v0, v1}, Landroidx/compose/runtime/MutableState;->setValue(Ljava/lang/Object;)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public static dlog(Ljava/lang/String;)V
|
||||
.locals 1
|
||||
|
||||
const-string v0, "RLHomeBackdrop"
|
||||
|
||||
invoke-static {v0, p0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method private static isDebugMenuStack()Z
|
||||
.locals 6
|
||||
|
||||
new-instance v0, Ljava/lang/Throwable;
|
||||
|
||||
invoke-direct {v0}, Ljava/lang/Throwable;-><init>()V
|
||||
|
||||
invoke-virtual {v0}, Ljava/lang/Throwable;->getStackTrace()[Ljava/lang/StackTraceElement;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
array-length v1, v0
|
||||
|
||||
const/4 v2, 0x0
|
||||
|
||||
:loop_start
|
||||
if-ge v2, v1, :loop_end
|
||||
|
||||
aget-object v3, v0, v2
|
||||
|
||||
invoke-virtual {v3}, Ljava/lang/StackTraceElement;->getClassName()Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
const-string v4, "debugmenu"
|
||||
|
||||
invoke-virtual {v3, v4}, Ljava/lang/String;->contains(Ljava/lang/CharSequence;)Z
|
||||
|
||||
move-result v5
|
||||
|
||||
if-eqz v5, :next
|
||||
|
||||
const/4 v0, 0x1
|
||||
|
||||
return v0
|
||||
|
||||
:next
|
||||
add-int/lit8 v2, v2, 0x1
|
||||
|
||||
goto :loop_start
|
||||
|
||||
:loop_end
|
||||
const/4 v0, 0x0
|
||||
|
||||
return v0
|
||||
.end method
|
||||
|
||||
|
||||
.method public static fromMiniPlayer(ILjava/lang/String;)V
|
||||
.locals 2
|
||||
|
||||
new-instance v0, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v1, "fromMiniPlayer: id="
|
||||
|
||||
invoke-direct {v0, v1}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v0, p0}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v1, " cover="
|
||||
|
||||
invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v0, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-static {v0}, Lradiant/HomeBackdrop;->dlog(Ljava/lang/String;)V
|
||||
|
||||
sput p0, Lradiant/HomeBackdrop;->currentAlbumId:I
|
||||
|
||||
sget-object v0, Lradiant/HomeBackdrop;->coverUuidState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
invoke-interface {v0, p1}, Landroidx/compose/runtime/MutableState;->setValue(Ljava/lang/Object;)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public static onTrack(Lcom/aspiro/wamp/model/Track;)V
|
||||
.locals 3
|
||||
|
||||
const-string v0, "onTrack: entered"
|
||||
|
||||
invoke-static {v0}, Lradiant/HomeBackdrop;->dlog(Ljava/lang/String;)V
|
||||
|
||||
if-eqz p0, :clear
|
||||
|
||||
:try_start
|
||||
invoke-virtual {p0}, Lcom/aspiro/wamp/model/MediaItem;->getAlbum()Lcom/aspiro/wamp/model/Album;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
if-eqz v0, :clear
|
||||
|
||||
invoke-virtual {v0}, Lcom/aspiro/wamp/model/Album;->getId()I
|
||||
|
||||
move-result v1
|
||||
|
||||
sput v1, Lradiant/HomeBackdrop;->currentAlbumId:I
|
||||
|
||||
invoke-virtual {v0}, Lcom/aspiro/wamp/model/Album;->getCover()Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
sget-object v1, Lradiant/HomeBackdrop;->coverUuidState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
invoke-interface {v1, v0}, Landroidx/compose/runtime/MutableState;->setValue(Ljava/lang/Object;)V
|
||||
|
||||
new-instance v1, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v2, "onTrack: set uuid="
|
||||
|
||||
invoke-direct {v1, v2}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-static {v0}, Lradiant/HomeBackdrop;->dlog(Ljava/lang/String;)V
|
||||
:try_end
|
||||
.catch Ljava/lang/Throwable; {:try_start .. :try_end} :swallow
|
||||
|
||||
return-void
|
||||
|
||||
:clear
|
||||
const-string v0, "onTrack: clearing (null track/album)"
|
||||
|
||||
invoke-static {v0}, Lradiant/HomeBackdrop;->dlog(Ljava/lang/String;)V
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput v0, Lradiant/HomeBackdrop;->currentAlbumId:I
|
||||
|
||||
sget-object v1, Lradiant/HomeBackdrop;->coverUuidState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
const/4 v2, 0x0
|
||||
|
||||
invoke-interface {v1, v2}, Landroidx/compose/runtime/MutableState;->setValue(Ljava/lang/Object;)V
|
||||
|
||||
return-void
|
||||
|
||||
:swallow
|
||||
const-string v0, "onTrack: caught throwable"
|
||||
|
||||
invoke-static {v0}, Lradiant/HomeBackdrop;->dlog(Ljava/lang/String;)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public static render(Landroidx/compose/runtime/Composer;I)V
|
||||
.locals 16
|
||||
.annotation build Landroidx/compose/runtime/Composable;
|
||||
.end annotation
|
||||
|
||||
invoke-static {}, Lradiant/HomeBackdrop;->isDebugMenuStack()Z
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :proceed
|
||||
|
||||
return-void
|
||||
|
||||
:proceed
|
||||
move-object/from16 v0, p0
|
||||
|
||||
const-string v1, "render: entered"
|
||||
|
||||
invoke-static {v1}, Lradiant/HomeBackdrop;->dlog(Ljava/lang/String;)V
|
||||
|
||||
const v1, 0x52414449
|
||||
|
||||
invoke-interface {v0, v1}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V
|
||||
|
||||
sget-object v1, Lradiant/HomeBackdrop;->coverUuidState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
invoke-interface {v1}, Landroidx/compose/runtime/State;->getValue()Ljava/lang/Object;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
check-cast v1, Ljava/lang/String;
|
||||
|
||||
new-instance v2, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v3, "render: uuid="
|
||||
|
||||
invoke-direct {v2, v3}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v2, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
invoke-static {v2}, Lradiant/HomeBackdrop;->dlog(Ljava/lang/String;)V
|
||||
|
||||
if-eqz v1, :skip
|
||||
|
||||
invoke-virtual {v1}, Ljava/lang/String;->length()I
|
||||
|
||||
move-result v2
|
||||
|
||||
if-eqz v2, :skip
|
||||
|
||||
# Render TIDAL's NATIVE DarkBlurBackground (p3.a)
|
||||
sget-object v8, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion;
|
||||
|
||||
const/4 v9, 0x0
|
||||
|
||||
const/4 v10, 0x1
|
||||
|
||||
const/4 v11, 0x0
|
||||
|
||||
invoke-static {v8, v9, v10, v11}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxSize$default(Landroidx/compose/ui/Modifier;FILjava/lang/Object;)Landroidx/compose/ui/Modifier;
|
||||
|
||||
move-result-object v13 # v13 = fillMaxSize modifier
|
||||
|
||||
sget v8, Lradiant/HomeBackdrop;->currentAlbumId:I # v8 = album id
|
||||
|
||||
move-object v9, v1 # v9 = cover uuid
|
||||
|
||||
# v10 = isPlaying, mirrored from the Player Backdrop variant
|
||||
sget v10, Lradiant/HomeBackdrop;->mode:I
|
||||
|
||||
const/4 v11, 0x2
|
||||
|
||||
if-ne v10, v11, :rl_mode_done
|
||||
|
||||
sget-object v10, Lradiant/HomeBackdrop;->isPlayingState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
invoke-interface {v10}, Landroidx/compose/runtime/State;->getValue()Ljava/lang/Object;
|
||||
|
||||
move-result-object v10
|
||||
|
||||
check-cast v10, Ljava/lang/Boolean;
|
||||
|
||||
invoke-virtual {v10}, Ljava/lang/Boolean;->booleanValue()Z
|
||||
|
||||
move-result v10
|
||||
|
||||
:rl_mode_done
|
||||
const/4 v11, 0x0 # v11 = isSeeking = false
|
||||
|
||||
const/4 v12, 0x0 # v12 = progress callback = null (unused while not seeking)
|
||||
|
||||
move-object v14, v0 # v14 = composer
|
||||
|
||||
const/4 v15, 0x0 # v15 = changed flags
|
||||
|
||||
invoke-static/range {v8 .. v15}, Lcom/tidal/android/feature/playerscreen/ui/composables/p3;->a(ILjava/lang/String;ZZLam0/a;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V
|
||||
|
||||
:skip
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,61 @@
|
||||
.class public final Lradiant/IsrcCache;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
# Caches trackId(String) -> ISRC(String), captured from TIDAL's own track mapper
|
||||
|
||||
.field public static final map:Ljava/util/concurrent/ConcurrentHashMap;
|
||||
|
||||
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
new-instance v0, Ljava/util/concurrent/ConcurrentHashMap;
|
||||
|
||||
invoke-direct {v0}, Ljava/util/concurrent/ConcurrentHashMap;-><init>()V
|
||||
|
||||
sput-object v0, Lradiant/IsrcCache;->map:Ljava/util/concurrent/ConcurrentHashMap;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static put(Ljava/lang/String;Ljava/lang/String;)V
|
||||
.locals 1
|
||||
|
||||
if-eqz p0, :ret
|
||||
|
||||
if-eqz p1, :ret
|
||||
|
||||
sget-object v0, Lradiant/IsrcCache;->map:Ljava/util/concurrent/ConcurrentHashMap;
|
||||
|
||||
invoke-virtual {v0, p0, p1}, Ljava/util/concurrent/ConcurrentHashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
|
||||
:ret
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static get(Ljava/lang/String;)Ljava/lang/String;
|
||||
.locals 2
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
if-eqz p0, :none
|
||||
|
||||
sget-object v1, Lradiant/IsrcCache;->map:Ljava/util/concurrent/ConcurrentHashMap;
|
||||
|
||||
invoke-virtual {v1, p0}, Ljava/util/concurrent/ConcurrentHashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
check-cast v0, Ljava/lang/String;
|
||||
|
||||
:none
|
||||
return-object v0
|
||||
.end method
|
||||
@@ -0,0 +1,27 @@
|
||||
.class public final Lradiant/LyricsReinitGuard;
|
||||
.super Ljava/lang/Object;
|
||||
.source "LyricsReinitGuard.smali"
|
||||
|
||||
|
||||
# static fields
|
||||
|
||||
# stuff that makes lyrics view stays open.. i forgot what it actually does other than short circuit a call or some shit <3
|
||||
.field public static lastMediaId:I
|
||||
|
||||
.field public static lastVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, -0x1
|
||||
|
||||
sput v0, Lradiant/LyricsReinitGuard;->lastMediaId:I
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-object v0, Lradiant/LyricsReinitGuard;->lastVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,139 @@
|
||||
.class public final Lradiant/MiniPlayerBackground;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static final colorState:Landroidx/compose/runtime/MutableState; # player bg color
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 2
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
const/4 v1, 0x2
|
||||
|
||||
invoke-static {v0, v0, v1, v0}, Landroidx/compose/runtime/SnapshotStateKt;->mutableStateOf$default(Ljava/lang/Object;Landroidx/compose/runtime/SnapshotMutationPolicy;ILjava/lang/Object;)Landroidx/compose/runtime/MutableState;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
sput-object v0, Lradiant/MiniPlayerBackground;->colorState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public static color(JLandroidx/compose/runtime/Composer;I)J
|
||||
.locals 8
|
||||
.annotation build Landroidx/compose/runtime/Composable;
|
||||
.end annotation
|
||||
|
||||
const v0, 0x4d504442 # compose group key
|
||||
|
||||
invoke-interface {p2, v0}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerBackground;->colorState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/MutableState;->getValue()Ljava/lang/Object; # read player color
|
||||
|
||||
move-result-object v0
|
||||
|
||||
check-cast v0, Landroidx/compose/ui/graphics/Color;
|
||||
|
||||
if-eqz v0, :fallback
|
||||
|
||||
invoke-virtual {v0}, Landroidx/compose/ui/graphics/Color;->unbox-impl()J
|
||||
|
||||
move-result-wide p0
|
||||
|
||||
:fallback
|
||||
const/16 v0, 0x2ee # 750ms
|
||||
|
||||
const/4 v1, 0x0
|
||||
|
||||
const/4 v2, 0x0
|
||||
|
||||
const/4 v3, 0x6
|
||||
|
||||
const/4 v4, 0x0
|
||||
|
||||
invoke-static {v0, v1, v2, v3, v4}, Landroidx/compose/animation/core/AnimationSpecKt;->tween$default(IILandroidx/compose/animation/core/Easing;ILjava/lang/Object;)Landroidx/compose/animation/core/TweenSpec;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
move-wide v0, p0
|
||||
|
||||
const-string v3, "miniPlayerBackgroundColorAnimation"
|
||||
|
||||
const/4 v4, 0x0
|
||||
|
||||
move-object v5, p2
|
||||
|
||||
const/16 v6, 0x1b0
|
||||
|
||||
const/16 v7, 0x8
|
||||
|
||||
invoke-static/range {v0 .. v7}, Landroidx/compose/animation/SingleValueAnimationKt;->animateColorAsState-euL9pac(JLandroidx/compose/animation/core/AnimationSpec;Ljava/lang/String;Lam0/l;Landroidx/compose/runtime/Composer;II)Landroidx/compose/runtime/State;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/State;->getValue()Ljava/lang/Object;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
check-cast v0, Landroidx/compose/ui/graphics/Color;
|
||||
|
||||
invoke-virtual {v0}, Landroidx/compose/ui/graphics/Color;->unbox-impl()J
|
||||
|
||||
move-result-wide p0
|
||||
|
||||
invoke-interface {p2}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V
|
||||
|
||||
return-wide p0
|
||||
.end method
|
||||
|
||||
|
||||
.method public static setHex(Ljava/lang/String;)V
|
||||
.locals 3
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
if-eqz p0, :set_color
|
||||
|
||||
:try_start
|
||||
invoke-static {p0}, Landroid/graphics/Color;->parseColor(Ljava/lang/String;)I
|
||||
|
||||
move-result p0
|
||||
|
||||
invoke-static {p0}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack ARGB color
|
||||
|
||||
move-result-wide v1
|
||||
|
||||
invoke-static {v1, v2}, Landroidx/compose/ui/graphics/Color;->box-impl(J)Landroidx/compose/ui/graphics/Color;
|
||||
|
||||
move-result-object v0
|
||||
:try_end
|
||||
.catch Ljava/lang/Throwable; {:try_start .. :try_end} :catch_parse
|
||||
|
||||
goto :set_color
|
||||
|
||||
:catch_parse
|
||||
move-exception p0
|
||||
|
||||
:set_color
|
||||
sget-object p0, Lradiant/MiniPlayerBackground;->colorState:Landroidx/compose/runtime/MutableState;
|
||||
|
||||
invoke-interface {p0, v0}, Landroidx/compose/runtime/MutableState;->setValue(Ljava/lang/Object;)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,62 @@
|
||||
.class public final Lradiant/MiniPlayerGestures$ApplyPending;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Ljava/lang/Runnable;
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lradiant/MiniPlayerGestures;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "ApplyPending"
|
||||
.end annotation
|
||||
|
||||
|
||||
# instance fields
|
||||
.field public final a:Landroidx/compose/material3/SheetState;
|
||||
|
||||
.field public final b:I
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>(Landroidx/compose/material3/SheetState;I)V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
iput-object p1, p0, Lradiant/MiniPlayerGestures$ApplyPending;->a:Landroidx/compose/material3/SheetState;
|
||||
|
||||
iput p2, p0, Lradiant/MiniPlayerGestures$ApplyPending;->b:I
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
# Apply once, then re schedule if there is still pending movement
|
||||
# -> Sheet anchors can appear one (or more) frames after opening the player, so this keeps applying the latest drag until no delta remains
|
||||
.method public final run()V
|
||||
.locals 3
|
||||
|
||||
iget-object v0, p0, Lradiant/MiniPlayerGestures$ApplyPending;->a:Landroidx/compose/material3/SheetState;
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniPlayerGestures;->applyPendingDrag(Landroidx/compose/material3/SheetState;)V
|
||||
|
||||
iget v1, p0, Lradiant/MiniPlayerGestures$ApplyPending;->b:I
|
||||
|
||||
add-int/lit8 v1, v1, -0x1
|
||||
|
||||
if-lez v1, :done
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniPlayerGestures;->needsApply(Landroidx/compose/material3/SheetState;)Z
|
||||
|
||||
move-result v2
|
||||
|
||||
if-eqz v2, :done
|
||||
|
||||
invoke-static {v0, v1}, Lradiant/MiniPlayerGestures;->scheduleApply(Landroidx/compose/material3/SheetState;I)V
|
||||
|
||||
:done
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,70 @@
|
||||
.class public final Lradiant/MiniPlayerGestures$FeedbackLayer;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Lam0/l;
|
||||
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lradiant/MiniPlayerGestures;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "FeedbackLayer"
|
||||
.end annotation
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static final INSTANCE:Lradiant/MiniPlayerGestures$FeedbackLayer;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
new-instance v0, Lradiant/MiniPlayerGestures$FeedbackLayer;
|
||||
|
||||
invoke-direct {v0}, Lradiant/MiniPlayerGestures$FeedbackLayer;-><init>()V
|
||||
|
||||
sput-object v0, Lradiant/MiniPlayerGestures$FeedbackLayer;->INSTANCE:Lradiant/MiniPlayerGestures$FeedbackLayer;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final invoke(Landroidx/compose/ui/graphics/GraphicsLayerScope;)Lkotlin/u;
|
||||
.locals 1
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerGestures;->v:Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F
|
||||
|
||||
move-result v0
|
||||
|
||||
invoke-interface {p1, v0}, Landroidx/compose/ui/graphics/GraphicsLayerScope;->setTranslationY(F)V
|
||||
|
||||
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
|
||||
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
.locals 0
|
||||
|
||||
check-cast p1, Landroidx/compose/ui/graphics/GraphicsLayerScope;
|
||||
|
||||
invoke-virtual {p0, p1}, Lradiant/MiniPlayerGestures$FeedbackLayer;->invoke(Landroidx/compose/ui/graphics/GraphicsLayerScope;)Lkotlin/u;
|
||||
|
||||
move-result-object p1
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
@@ -0,0 +1,60 @@
|
||||
.class public final Lradiant/MiniPlayerGestures$FeedbackResetAnimator;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Landroid/animation/ValueAnimator$AnimatorUpdateListener;
|
||||
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lradiant/MiniPlayerGestures;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "FeedbackResetAnimator"
|
||||
.end annotation
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final onAnimationUpdate(Landroid/animation/ValueAnimator;)V
|
||||
.locals 3
|
||||
|
||||
move-object v0, p1
|
||||
|
||||
invoke-virtual {p1}, Landroid/animation/ValueAnimator;->getAnimatedValue()Ljava/lang/Object;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
check-cast v1, Ljava/lang/Float;
|
||||
|
||||
invoke-virtual {v1}, Ljava/lang/Float;->floatValue()F
|
||||
|
||||
move-result v1
|
||||
|
||||
invoke-static {v1}, Lradiant/MiniPlayerGestures;->setSwipeFeedbackDirect(F)V
|
||||
|
||||
invoke-virtual {v0}, Landroid/animation/ValueAnimator;->getAnimatedFraction()F
|
||||
|
||||
move-result v1
|
||||
|
||||
const/high16 v2, 0x3f800000 # 1.0f
|
||||
|
||||
cmpg-float v1, v1, v2
|
||||
|
||||
if-gez v1, :done
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniPlayerGestures;->clearSwipeFeedbackAnimator(Landroid/animation/ValueAnimator;)V
|
||||
|
||||
:done
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,521 @@
|
||||
.class public final Lradiant/MiniPlayerGestures$Gesture;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Lam0/l;
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lradiant/MiniPlayerGestures;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "Gesture"
|
||||
.end annotation
|
||||
|
||||
|
||||
# instance fields
|
||||
.field public final a:Lam0/l;
|
||||
|
||||
# Y at ACTION_DOWN
|
||||
.field public b:F
|
||||
|
||||
# Last Y seen (on this pointer stream)
|
||||
.field public c:F
|
||||
|
||||
# True while this listener owns an active pointer stream
|
||||
.field public d:Z
|
||||
|
||||
# True when sheet drag has started
|
||||
.field public e:Z
|
||||
|
||||
.field public final f:Landroidx/compose/material3/SheetState;
|
||||
|
||||
.field public g:F
|
||||
|
||||
# X at ACTION_DOWN
|
||||
.field public i:F
|
||||
|
||||
# True once horizontal movement wins (prevents later vertical start)
|
||||
.field public j:Z
|
||||
|
||||
# Drag option enabled
|
||||
.field public final k:Z
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>(Lam0/l;Landroidx/compose/material3/SheetState;Z)V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
iput-object p1, p0, Lradiant/MiniPlayerGestures$Gesture;->a:Lam0/l;
|
||||
|
||||
iput-object p2, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||
|
||||
iput-boolean p3, p0, Lradiant/MiniPlayerGestures$Gesture;->k:Z
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private static dp(F)F
|
||||
.locals 1
|
||||
|
||||
invoke-static {}, Landroid/content/res/Resources;->getSystem()Landroid/content/res/Resources;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-virtual {v0}, Landroid/content/res/Resources;->getDisplayMetrics()Landroid/util/DisplayMetrics;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
iget v0, v0, Landroid/util/DisplayMetrics;->density:F
|
||||
|
||||
mul-float/2addr p0, v0
|
||||
|
||||
return p0
|
||||
.end method
|
||||
|
||||
.method private dragSheet(FJ)V
|
||||
.locals 1
|
||||
|
||||
iget-object v0, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||
|
||||
invoke-static {v0, p1, p2, p3}, Lradiant/MiniPlayerGestures;->updateDragTimed(Landroidx/compose/material3/SheetState;FJ)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private finishSheet(F)V
|
||||
.locals 2
|
||||
|
||||
iget-object v0, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||
|
||||
iget-object v1, p0, Lradiant/MiniPlayerGestures$Gesture;->a:Lam0/l;
|
||||
|
||||
invoke-static {v0, v1, p1}, Lradiant/MiniPlayerGestures;->finishDrag(Landroidx/compose/material3/SheetState;Lam0/l;F)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private openPlayer()V
|
||||
.locals 2
|
||||
|
||||
iget-object v0, p0, Lradiant/MiniPlayerGestures$Gesture;->a:Lam0/l;
|
||||
|
||||
sget-object v1, Lcom/tidal/android/feature/appscaffold/ui/b$b;->a:Lcom/tidal/android/feature/appscaffold/ui/b$b;
|
||||
|
||||
invoke-interface {v0, v1}, Lam0/l;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private reset()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->d:Z
|
||||
|
||||
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||
|
||||
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->j:Z
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->i:F
|
||||
|
||||
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->g:F
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private startSheet(FJ)V
|
||||
.locals 2
|
||||
|
||||
# empty player guard
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->hasMiniPlayerMedia()Z
|
||||
|
||||
move-result v0
|
||||
|
||||
if-nez v0, :start_guard
|
||||
|
||||
return-void
|
||||
|
||||
:start_guard
|
||||
|
||||
# avoid starting twice
|
||||
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||
|
||||
if-eqz v0, :start
|
||||
|
||||
return-void
|
||||
|
||||
:start
|
||||
const/4 v0, 0x1
|
||||
|
||||
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||
|
||||
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->k:Z
|
||||
|
||||
if-eqz v0, :done
|
||||
|
||||
iget-object v1, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||
|
||||
iget v0, p0, Lradiant/MiniPlayerGestures$Gesture;->b:F
|
||||
|
||||
invoke-static {v1, p1, v0, p2, p3}, Lradiant/MiniPlayerGestures;->beginDrag(Landroidx/compose/material3/SheetState;FFJ)V
|
||||
|
||||
invoke-direct {p0}, Lradiant/MiniPlayerGestures$Gesture;->openPlayer()V
|
||||
|
||||
:done
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
# Listens for MotionEvents
|
||||
.method public final invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||
.locals 11
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getActionMasked()I
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :down
|
||||
|
||||
const/4 v1, 0x1
|
||||
|
||||
if-eq v0, v1, :up
|
||||
|
||||
const/4 v1, 0x2
|
||||
|
||||
if-eq v0, v1, :move
|
||||
|
||||
const/4 v1, 0x3
|
||||
|
||||
if-eq v0, v1, :cancel
|
||||
|
||||
goto :done
|
||||
|
||||
# ACTION_DOWN
|
||||
:down
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->resetGestureLock()V
|
||||
|
||||
invoke-static {p1}, Lradiant/MiniPlayerGestures;->beginMotion(Landroid/view/MotionEvent;)V
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v0
|
||||
|
||||
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->b:F
|
||||
|
||||
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->c:F
|
||||
|
||||
const/4 v0, 0x1
|
||||
|
||||
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->d:Z
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->g:F
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||
|
||||
move-result v0
|
||||
|
||||
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->i:F
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
iput-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->j:Z
|
||||
|
||||
iget v0, p0, Lradiant/MiniPlayerGestures$Gesture;->b:F
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniPlayerGestures;->beginSwipeGesture(F)V
|
||||
|
||||
:skip_feedback_down
|
||||
|
||||
goto :done
|
||||
|
||||
# ACTION_MOVE
|
||||
:move
|
||||
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->d:Z
|
||||
|
||||
if-eqz v0, :done
|
||||
|
||||
invoke-static {p1}, Lradiant/MiniPlayerGestures;->trackMotion(Landroid/view/MotionEvent;)V
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v0
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getEventTime()J
|
||||
|
||||
move-result-wide v5
|
||||
|
||||
iget v1, p0, Lradiant/MiniPlayerGestures$Gesture;->c:F
|
||||
|
||||
sub-float v1, v0, v1
|
||||
|
||||
iput v1, p0, Lradiant/MiniPlayerGestures$Gesture;->g:F
|
||||
|
||||
iget v2, p0, Lradiant/MiniPlayerGestures$Gesture;->b:F
|
||||
|
||||
sub-float v2, v0, v2
|
||||
|
||||
iget-boolean v3, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||
|
||||
if-eqz v3, :maybe_start
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->suppressHorizontalGestures()V
|
||||
|
||||
iget-boolean v3, p0, Lradiant/MiniPlayerGestures$Gesture;->k:Z
|
||||
|
||||
if-eqz v3, :feedback_active
|
||||
|
||||
invoke-direct {p0, v2, v5, v6}, Lradiant/MiniPlayerGestures$Gesture;->dragSheet(FJ)V
|
||||
|
||||
:feedback_active
|
||||
invoke-static {v2}, Lradiant/MiniPlayerGestures;->setSwipeFeedbackFromDrag(F)V
|
||||
|
||||
goto :store_last
|
||||
|
||||
:maybe_start
|
||||
iget-boolean v3, p0, Lradiant/MiniPlayerGestures$Gesture;->j:Z
|
||||
|
||||
if-eqz v3, :axis_check
|
||||
|
||||
goto :store_last
|
||||
|
||||
:axis_check
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||
|
||||
move-result v7
|
||||
|
||||
iget v8, p0, Lradiant/MiniPlayerGestures$Gesture;->i:F
|
||||
|
||||
sub-float/2addr v7, v8
|
||||
|
||||
invoke-static {v7}, Ljava/lang/Math;->abs(F)F
|
||||
|
||||
move-result v7
|
||||
|
||||
invoke-static {v2}, Ljava/lang/Math;->abs(F)F
|
||||
|
||||
move-result v8
|
||||
|
||||
const/high16 v9, 0x40c00000 # 6.0f axis deadzone
|
||||
|
||||
invoke-static {v9}, Lradiant/MiniPlayerGestures$Gesture;->dp(F)F
|
||||
|
||||
move-result v9
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->isHorizontalGestureLocked()Z
|
||||
|
||||
move-result v10
|
||||
|
||||
if-eqz v10, :horizontal_check
|
||||
|
||||
const/4 v10, 0x0
|
||||
|
||||
invoke-static {v10}, Lradiant/MiniPlayerGestures;->setSwipeFeedbackDirect(F)V
|
||||
|
||||
goto :store_last
|
||||
|
||||
:horizontal_check
|
||||
|
||||
cmpg-float v10, v7, v9
|
||||
|
||||
if-gez v10, :horizontal_dominance
|
||||
|
||||
goto :vertical_check
|
||||
|
||||
:horizontal_dominance
|
||||
|
||||
const v10, 0x3f8ccccd # 1.1f
|
||||
|
||||
mul-float/2addr v10, v8
|
||||
|
||||
cmpg-float v10, v7, v10
|
||||
|
||||
if-gez v10, :horizontal_wins
|
||||
|
||||
goto :vertical_check
|
||||
|
||||
:horizontal_wins
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->lockHorizontalGesture()Z
|
||||
|
||||
move-result v10
|
||||
|
||||
if-eqz v10, :store_last
|
||||
|
||||
const/4 v10, 0x1
|
||||
|
||||
iput-boolean v10, p0, Lradiant/MiniPlayerGestures$Gesture;->j:Z
|
||||
|
||||
const/4 v10, 0x0
|
||||
|
||||
invoke-static {v10}, Lradiant/MiniPlayerGestures;->setSwipeFeedbackDirect(F)V
|
||||
|
||||
goto :store_last
|
||||
|
||||
# At least 16dp up to open the player
|
||||
:vertical_check
|
||||
neg-float v9, v9
|
||||
|
||||
cmpg-float v10, v2, v9
|
||||
|
||||
if-lez v10, :vertical_dominance
|
||||
|
||||
invoke-static {v2}, Lradiant/MiniPlayerGestures;->setSwipeFeedbackFromDrag(F)V
|
||||
|
||||
goto :store_last
|
||||
|
||||
:vertical_dominance
|
||||
const v10, 0x3f8ccccd # 1.1f -> early vertical axis lock
|
||||
|
||||
mul-float/2addr v7, v10
|
||||
|
||||
cmpl-float v10, v8, v7
|
||||
|
||||
if-gtz v10, :begin
|
||||
|
||||
goto :store_last
|
||||
|
||||
:begin
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->lockVerticalGesture()Z
|
||||
|
||||
move-result v10
|
||||
|
||||
if-eqz v10, :store_last
|
||||
|
||||
invoke-direct {p0, v2, v5, v6}, Lradiant/MiniPlayerGestures$Gesture;->startSheet(FJ)V
|
||||
|
||||
iget-boolean v10, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||
|
||||
if-eqz v10, :store_last
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->suppressHorizontalGestures()V
|
||||
|
||||
iget-boolean v10, p0, Lradiant/MiniPlayerGestures$Gesture;->k:Z
|
||||
|
||||
if-eqz v10, :feedback_begin
|
||||
|
||||
invoke-direct {p0, v2, v5, v6}, Lradiant/MiniPlayerGestures$Gesture;->dragSheet(FJ)V
|
||||
|
||||
:feedback_begin
|
||||
invoke-static {v2}, Lradiant/MiniPlayerGestures;->setSwipeFeedbackFromDrag(F)V
|
||||
|
||||
|
||||
:store_last
|
||||
iput v0, p0, Lradiant/MiniPlayerGestures$Gesture;->c:F
|
||||
|
||||
goto :done
|
||||
|
||||
# ACTION_UP
|
||||
:up
|
||||
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->d:Z
|
||||
|
||||
if-eqz v0, :done
|
||||
|
||||
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||
|
||||
if-eqz v0, :up_no_drag
|
||||
|
||||
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->k:Z
|
||||
|
||||
if-eqz v0, :open_simple
|
||||
|
||||
invoke-static {p1}, Lradiant/MiniPlayerGestures;->trackMotion(Landroid/view/MotionEvent;)V
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v0
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getEventTime()J
|
||||
|
||||
move-result-wide v5
|
||||
|
||||
iget-object v1, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||
|
||||
invoke-static {v1, v0, v5, v6}, Lradiant/MiniPlayerGestures;->updateDragToYTimed(Landroidx/compose/material3/SheetState;FJ)V
|
||||
|
||||
invoke-static {v1}, Lradiant/MiniPlayerGestures;->releaseVelocity(Landroidx/compose/material3/SheetState;)F
|
||||
|
||||
move-result v0
|
||||
|
||||
invoke-direct {p0, v0}, Lradiant/MiniPlayerGestures$Gesture;->finishSheet(F)V
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->animateSwipeFeedbackReset()V
|
||||
|
||||
goto :finish
|
||||
|
||||
:up_no_drag
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->animateSwipeFeedbackReset()V
|
||||
|
||||
goto :finish
|
||||
|
||||
:open_simple
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->suppressHorizontalGestures()V
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v0
|
||||
|
||||
iget-object v1, p0, Lradiant/MiniPlayerGestures$Gesture;->a:Lam0/l;
|
||||
|
||||
invoke-static {v1, v0}, Lradiant/MiniPlayerGestures;->completeSimpleSwipe(Lam0/l;F)Z
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->animateSwipeFeedbackReset()V
|
||||
|
||||
|
||||
:finish
|
||||
invoke-direct {p0}, Lradiant/MiniPlayerGestures$Gesture;->reset()V
|
||||
|
||||
goto :done
|
||||
|
||||
# ACTION_CANCEL
|
||||
:cancel
|
||||
iget-boolean v0, p0, Lradiant/MiniPlayerGestures$Gesture;->e:Z
|
||||
|
||||
if-eqz v0, :cancel_no_active
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->animateSwipeFeedbackReset()V
|
||||
|
||||
iget-object v0, p0, Lradiant/MiniPlayerGestures$Gesture;->f:Landroidx/compose/material3/SheetState;
|
||||
|
||||
iget-object v1, p0, Lradiant/MiniPlayerGestures$Gesture;->a:Lam0/l;
|
||||
|
||||
invoke-static {v0, v1}, Lradiant/MiniPlayerGestures;->cancelDrag(Landroidx/compose/material3/SheetState;Lam0/l;)V
|
||||
|
||||
goto :cancel_reset
|
||||
|
||||
:cancel_no_active
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->animateSwipeFeedbackReset()V
|
||||
|
||||
:cancel_reset
|
||||
invoke-direct {p0}, Lradiant/MiniPlayerGestures$Gesture;->reset()V
|
||||
|
||||
|
||||
:done
|
||||
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
|
||||
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
.locals 0
|
||||
|
||||
check-cast p1, Landroid/view/MotionEvent;
|
||||
|
||||
invoke-virtual {p0, p1}, Lradiant/MiniPlayerGestures$Gesture;->invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||
|
||||
move-result-object p1
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
@@ -0,0 +1,178 @@
|
||||
.class public final Lradiant/MiniPlayerGestures$RootGesture;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Lam0/l;
|
||||
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lradiant/MiniPlayerGestures;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "RootGesture"
|
||||
.end annotation
|
||||
|
||||
|
||||
# instance fields
|
||||
.field public final a:Lam0/l;
|
||||
|
||||
.field public final b:Landroidx/compose/material3/SheetState;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>(Lam0/l;Landroidx/compose/material3/SheetState;)V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
iput-object p1, p0, Lradiant/MiniPlayerGestures$RootGesture;->a:Lam0/l;
|
||||
|
||||
iput-object p2, p0, Lradiant/MiniPlayerGestures$RootGesture;->b:Landroidx/compose/material3/SheetState;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||
.locals 6
|
||||
|
||||
iget-object v0, p0, Lradiant/MiniPlayerGestures$RootGesture;->b:Landroidx/compose/material3/SheetState;
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getActionMasked()I
|
||||
|
||||
move-result v1
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniPlayerGestures;->isDragging(Landroidx/compose/material3/SheetState;)Z
|
||||
|
||||
move-result v2
|
||||
|
||||
if-eqz v2, :maybe_reset_only
|
||||
|
||||
const/4 v2, 0x1
|
||||
|
||||
if-eq v1, v2, :up
|
||||
|
||||
const/4 v2, 0x2
|
||||
|
||||
if-eq v1, v2, :move
|
||||
|
||||
const/4 v2, 0x3
|
||||
|
||||
if-eq v1, v2, :cancel
|
||||
|
||||
goto :done
|
||||
|
||||
:maybe_reset_only
|
||||
const/4 v2, 0x1
|
||||
|
||||
if-eq v1, v2, :simple_up
|
||||
|
||||
const/4 v2, 0x3
|
||||
|
||||
if-eq v1, v2, :reset_only
|
||||
|
||||
const/4 v2, 0x2
|
||||
|
||||
if-eq v1, v2, :simple_move
|
||||
|
||||
goto :done
|
||||
|
||||
:reset_only
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->resetGestureLock()V
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->animateSwipeFeedbackReset()V
|
||||
|
||||
goto :done
|
||||
|
||||
:simple_move
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v1
|
||||
|
||||
invoke-static {v1}, Lradiant/MiniPlayerGestures;->updateSimpleSwipe(F)Z
|
||||
|
||||
goto :done
|
||||
|
||||
:simple_up
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v1
|
||||
|
||||
iget-object v2, p0, Lradiant/MiniPlayerGestures$RootGesture;->a:Lam0/l;
|
||||
|
||||
invoke-static {v2, v1}, Lradiant/MiniPlayerGestures;->completeSimpleSwipe(Lam0/l;F)Z
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->animateSwipeFeedbackReset()V
|
||||
|
||||
goto :done
|
||||
|
||||
|
||||
# ACTION_MOVE
|
||||
:move
|
||||
invoke-static {p1}, Lradiant/MiniPlayerGestures;->trackMotion(Landroid/view/MotionEvent;)V
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v2
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getEventTime()J
|
||||
|
||||
move-result-wide v4
|
||||
|
||||
invoke-static {v0, v2, v4, v5}, Lradiant/MiniPlayerGestures;->updateDragToYTimed(Landroidx/compose/material3/SheetState;FJ)V
|
||||
|
||||
goto :done
|
||||
|
||||
# ACTION_UP
|
||||
:up
|
||||
invoke-static {p1}, Lradiant/MiniPlayerGestures;->trackMotion(Landroid/view/MotionEvent;)V
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v1
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getEventTime()J
|
||||
|
||||
move-result-wide v4
|
||||
|
||||
invoke-static {v0, v1, v4, v5}, Lradiant/MiniPlayerGestures;->updateDragToYTimed(Landroidx/compose/material3/SheetState;FJ)V
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniPlayerGestures;->releaseVelocity(Landroidx/compose/material3/SheetState;)F
|
||||
|
||||
move-result v1
|
||||
|
||||
iget-object v2, p0, Lradiant/MiniPlayerGestures$RootGesture;->a:Lam0/l;
|
||||
|
||||
invoke-static {v0, v2, v1}, Lradiant/MiniPlayerGestures;->finishDrag(Landroidx/compose/material3/SheetState;Lam0/l;F)V
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->animateSwipeFeedbackReset()V
|
||||
|
||||
goto :done
|
||||
|
||||
# ACTION_CANCEL
|
||||
:cancel
|
||||
iget-object v2, p0, Lradiant/MiniPlayerGestures$RootGesture;->a:Lam0/l;
|
||||
|
||||
invoke-static {v0, v2}, Lradiant/MiniPlayerGestures;->cancelDrag(Landroidx/compose/material3/SheetState;Lam0/l;)V
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->animateSwipeFeedbackReset()V
|
||||
|
||||
:done
|
||||
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
|
||||
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
.locals 0
|
||||
|
||||
check-cast p1, Landroid/view/MotionEvent;
|
||||
|
||||
invoke-virtual {p0, p1}, Lradiant/MiniPlayerGestures$RootGesture;->invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||
|
||||
move-result-object p1
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,150 @@
|
||||
.class public final Lradiant/MiniPlayerTrackGestures$Gesture;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Lam0/l;
|
||||
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lradiant/MiniPlayerTrackGestures;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "Gesture"
|
||||
.end annotation
|
||||
|
||||
|
||||
# instance fields
|
||||
.field public final a:Lam0/l;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>(Lam0/l;)V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
iput-object p1, p0, Lradiant/MiniPlayerTrackGestures$Gesture;->a:Lam0/l;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private nextTrack()V
|
||||
.locals 2
|
||||
|
||||
iget-object v0, p0, Lradiant/MiniPlayerTrackGestures$Gesture;->a:Lam0/l;
|
||||
|
||||
sget-object v1, Lcom/tidal/android/feature/appscaffold/ui/r$a;->a:Lcom/tidal/android/feature/appscaffold/ui/r$a; # Tidal's built-in next track mini-player event
|
||||
|
||||
invoke-interface {v0, v1}, Lam0/l;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private previousTrack()V
|
||||
.locals 2
|
||||
|
||||
iget-object v0, p0, Lradiant/MiniPlayerTrackGestures$Gesture;->a:Lam0/l;
|
||||
|
||||
sget-object v1, Lcom/tidal/android/feature/appscaffold/ui/r$c;->a:Lcom/tidal/android/feature/appscaffold/ui/r$c; # Our synthetic previous track event
|
||||
|
||||
invoke-interface {v0, v1}, Lam0/l;->invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||
.locals 3
|
||||
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getActionMasked()I
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :down
|
||||
|
||||
const/4 v1, 0x1
|
||||
|
||||
if-eq v0, v1, :up
|
||||
|
||||
const/4 v1, 0x2
|
||||
|
||||
if-eq v0, v1, :move
|
||||
|
||||
const/4 v1, 0x3
|
||||
|
||||
if-eq v0, v1, :cancel
|
||||
|
||||
goto :done
|
||||
|
||||
# ACTION_DOWN
|
||||
:down
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||
|
||||
move-result v0
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v1
|
||||
invoke-static {v0, v1}, Lradiant/MiniPlayerTrackGestures;->beginDrag(FF)V
|
||||
goto :done
|
||||
|
||||
# ACTION_MOVE
|
||||
:move
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||
|
||||
move-result v0
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v1
|
||||
invoke-static {v0, v1}, Lradiant/MiniPlayerTrackGestures;->moveDrag(FF)V
|
||||
goto :done
|
||||
|
||||
# ACTION_UP
|
||||
:up
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawX()F
|
||||
|
||||
move-result v0
|
||||
invoke-virtual {p1}, Landroid/view/MotionEvent;->getRawY()F
|
||||
|
||||
move-result v1
|
||||
invoke-static {v0, v1}, Lradiant/MiniPlayerTrackGestures;->finishDrag(FF)I
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :done
|
||||
|
||||
# Negative result = swipe left -> next. Positive result = swipe right -> previous
|
||||
if-gez v0, :previous
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->suppressTapOpen()V
|
||||
invoke-direct {p0}, Lradiant/MiniPlayerTrackGestures$Gesture;->nextTrack()V
|
||||
goto :done
|
||||
|
||||
:previous
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->suppressTapOpen()V
|
||||
invoke-direct {p0}, Lradiant/MiniPlayerTrackGestures$Gesture;->previousTrack()V
|
||||
goto :done
|
||||
|
||||
# ACTION_CANCEL
|
||||
:cancel
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->cancelDrag()V
|
||||
|
||||
:done
|
||||
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
|
||||
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
.locals 0
|
||||
|
||||
check-cast p1, Landroid/view/MotionEvent;
|
||||
|
||||
invoke-virtual {p0, p1}, Lradiant/MiniPlayerTrackGestures$Gesture;->invoke(Landroid/view/MotionEvent;)Lkotlin/u;
|
||||
|
||||
move-result-object p1
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
@@ -0,0 +1,70 @@
|
||||
.class public final Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Lam0/l;
|
||||
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lradiant/MiniPlayerTrackGestures;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "OffsetLayer"
|
||||
.end annotation
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static final INSTANCE:Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
new-instance v0, Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||
|
||||
invoke-direct {v0}, Lradiant/MiniPlayerTrackGestures$OffsetLayer;-><init>()V
|
||||
|
||||
sput-object v0, Lradiant/MiniPlayerTrackGestures$OffsetLayer;->INSTANCE:Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final invoke(Landroidx/compose/ui/graphics/GraphicsLayerScope;)Lkotlin/u;
|
||||
.locals 1
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F
|
||||
|
||||
move-result v0
|
||||
|
||||
invoke-interface {p1, v0}, Landroidx/compose/ui/graphics/GraphicsLayerScope;->setTranslationX(F)V
|
||||
|
||||
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
|
||||
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
.locals 0
|
||||
|
||||
check-cast p1, Landroidx/compose/ui/graphics/GraphicsLayerScope;
|
||||
|
||||
invoke-virtual {p0, p1}, Lradiant/MiniPlayerTrackGestures$OffsetLayer;->invoke(Landroidx/compose/ui/graphics/GraphicsLayerScope;)Lkotlin/u;
|
||||
|
||||
move-result-object p1
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
@@ -0,0 +1,60 @@
|
||||
.class public final Lradiant/MiniPlayerTrackGestures$ResetAnimator;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Landroid/animation/ValueAnimator$AnimatorUpdateListener;
|
||||
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lradiant/MiniPlayerTrackGestures;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "ResetAnimator"
|
||||
.end annotation
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final onAnimationUpdate(Landroid/animation/ValueAnimator;)V
|
||||
.locals 3
|
||||
|
||||
move-object v0, p1
|
||||
|
||||
invoke-virtual {p1}, Landroid/animation/ValueAnimator;->getAnimatedValue()Ljava/lang/Object;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
check-cast v1, Ljava/lang/Float;
|
||||
|
||||
invoke-virtual {v1}, Ljava/lang/Float;->floatValue()F
|
||||
|
||||
move-result v1
|
||||
|
||||
invoke-static {v1}, Lradiant/MiniPlayerTrackGestures;->setDragOffsetDirect(F)V
|
||||
|
||||
invoke-virtual {v0}, Landroid/animation/ValueAnimator;->getAnimatedFraction()F
|
||||
|
||||
move-result v1
|
||||
|
||||
const/high16 v2, 0x3f800000 # 1.0f
|
||||
|
||||
cmpg-float v1, v1, v2
|
||||
|
||||
if-gez v1, :done
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniPlayerTrackGestures;->clearResetAnimator(Landroid/animation/ValueAnimator;)V
|
||||
|
||||
:done
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,140 @@
|
||||
.class public final Lradiant/MiniPlayerTrackGestures$TextDraw;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Lam0/l;
|
||||
|
||||
|
||||
# annotations
|
||||
.annotation system Ldalvik/annotation/EnclosingClass;
|
||||
value = Lradiant/MiniPlayerTrackGestures;
|
||||
.end annotation
|
||||
|
||||
.annotation system Ldalvik/annotation/InnerClass;
|
||||
accessFlags = 0x19
|
||||
name = "TextDraw"
|
||||
.end annotation
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static final INSTANCE:Lradiant/MiniPlayerTrackGestures$TextDraw;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
new-instance v0, Lradiant/MiniPlayerTrackGestures$TextDraw;
|
||||
|
||||
invoke-direct {v0}, Lradiant/MiniPlayerTrackGestures$TextDraw;-><init>()V
|
||||
|
||||
sput-object v0, Lradiant/MiniPlayerTrackGestures$TextDraw;->INSTANCE:Lradiant/MiniPlayerTrackGestures$TextDraw;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final invoke(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)Lkotlin/u;
|
||||
.locals 12
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F
|
||||
|
||||
move-result v10
|
||||
|
||||
invoke-interface {p1}, Landroidx/compose/ui/graphics/drawscope/DrawScope;->getDrawContext()Landroidx/compose/ui/graphics/drawscope/DrawContext;
|
||||
|
||||
move-result-object v7
|
||||
|
||||
invoke-interface {v7}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->getSize-NH-jbRc()J
|
||||
|
||||
move-result-wide v8
|
||||
|
||||
const/16 v0, 0x20
|
||||
|
||||
shr-long v1, v8, v0
|
||||
|
||||
long-to-int v1, v1
|
||||
|
||||
invoke-static {v1}, Ljava/lang/Float;->intBitsToFloat(I)F
|
||||
|
||||
move-result v4
|
||||
|
||||
invoke-interface {v7}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->getCanvas()Landroidx/compose/ui/graphics/Canvas;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/ui/graphics/Canvas;->save()V
|
||||
|
||||
:try_start_0
|
||||
sget-object v0, Landroidx/compose/ui/graphics/ClipOp;->Companion:Landroidx/compose/ui/graphics/ClipOp$Companion;
|
||||
|
||||
invoke-virtual {v0}, Landroidx/compose/ui/graphics/ClipOp$Companion;->getIntersect-rtfAjoo()I
|
||||
|
||||
move-result v6
|
||||
|
||||
invoke-interface {v7}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->getTransform()Landroidx/compose/ui/graphics/drawscope/DrawTransform;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
const v2, -0x800001 # -Float.MAX_VALUE -> unbounded left
|
||||
|
||||
const v3, -0x800001 # -Float.MAX_VALUE -> unbounded top
|
||||
|
||||
const v5, 0x7f7fffff # Float.MAX_VALUE -> unbounded bottom
|
||||
|
||||
invoke-interface/range {v1 .. v6}, Landroidx/compose/ui/graphics/drawscope/DrawTransform;->clipRect-N_I0leg(FFFFI)V
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
invoke-interface {v1, v10, v0}, Landroidx/compose/ui/graphics/drawscope/DrawTransform;->translate(FF)V
|
||||
|
||||
invoke-interface {p1}, Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;->drawContent()V
|
||||
:try_end_0
|
||||
.catchall {:try_start_0 .. :try_end_0} :catchall_0
|
||||
|
||||
invoke-interface {v7}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->getCanvas()Landroidx/compose/ui/graphics/Canvas;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/ui/graphics/Canvas;->restore()V
|
||||
|
||||
invoke-interface {v7, v8, v9}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->setSize-uvyYCjk(J)V
|
||||
|
||||
sget-object p1, Lkotlin/u;->a:Lkotlin/u;
|
||||
|
||||
return-object p1
|
||||
|
||||
:catchall_0
|
||||
move-exception v0
|
||||
|
||||
invoke-interface {v7}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->getCanvas()Landroidx/compose/ui/graphics/Canvas;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
invoke-interface {v1}, Landroidx/compose/ui/graphics/Canvas;->restore()V
|
||||
|
||||
invoke-interface {v7, v8, v9}, Landroidx/compose/ui/graphics/drawscope/DrawContext;->setSize-uvyYCjk(J)V
|
||||
|
||||
throw v0
|
||||
.end method
|
||||
|
||||
.method public bridge synthetic invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
.locals 0
|
||||
|
||||
check-cast p1, Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;
|
||||
|
||||
invoke-virtual {p0, p1}, Lradiant/MiniPlayerTrackGestures$TextDraw;->invoke(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)Lkotlin/u;
|
||||
|
||||
move-result-object p1
|
||||
|
||||
return-object p1
|
||||
.end method
|
||||
@@ -0,0 +1,605 @@
|
||||
.class public final Lradiant/MiniPlayerTrackGestures;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field private static a:J
|
||||
|
||||
.field public static b:Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
.field private static c:Landroid/animation/ValueAnimator;
|
||||
|
||||
.field private static d:Ljava/lang/Object;
|
||||
|
||||
# X at ACTION_DOWN
|
||||
.field private static e:F
|
||||
|
||||
# Y at ACTION_DOWN
|
||||
.field private static f:F
|
||||
|
||||
.field private static g:Z
|
||||
|
||||
.field private static h:Z
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
invoke-static {v0}, Landroidx/compose/runtime/PrimitiveSnapshotStateKt;->mutableFloatStateOf(F)Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
sput-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static animateReset()V
|
||||
.locals 7
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||
|
||||
if-eqz v0, :read_offset
|
||||
|
||||
invoke-virtual {v0}, Landroid/animation/ValueAnimator;->cancel()V
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||
|
||||
:read_offset
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F
|
||||
|
||||
move-result v0
|
||||
|
||||
invoke-static {v0}, Ljava/lang/Math;->abs(F)F
|
||||
|
||||
move-result v1
|
||||
|
||||
const/4 v3, 0x0
|
||||
|
||||
const/high16 v2, 0x3f000000 # 0.5f
|
||||
|
||||
cmpg-float v1, v1, v2
|
||||
|
||||
if-lez v1, :snap_zero
|
||||
|
||||
const/4 v1, 0x2
|
||||
|
||||
new-array v1, v1, [F
|
||||
|
||||
const/4 v2, 0x0
|
||||
|
||||
aput v0, v1, v2
|
||||
|
||||
const/4 v0, 0x1
|
||||
|
||||
aput v3, v1, v0
|
||||
|
||||
invoke-static {v1}, Landroid/animation/ValueAnimator;->ofFloat([F)Landroid/animation/ValueAnimator;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
const-wide/16 v4, 0xb4 # 180ms
|
||||
|
||||
invoke-virtual {v1, v4, v5}, Landroid/animation/ValueAnimator;->setDuration(J)Landroid/animation/ValueAnimator;
|
||||
|
||||
new-instance v0, Landroid/view/animation/DecelerateInterpolator;
|
||||
|
||||
const/high16 v4, 0x3fc00000 # 1.5f
|
||||
|
||||
invoke-direct {v0, v4}, Landroid/view/animation/DecelerateInterpolator;-><init>(F)V
|
||||
|
||||
invoke-virtual {v1, v0}, Landroid/animation/ValueAnimator;->setInterpolator(Landroid/animation/TimeInterpolator;)V
|
||||
|
||||
new-instance v0, Lradiant/MiniPlayerTrackGestures$ResetAnimator;
|
||||
|
||||
invoke-direct {v0}, Lradiant/MiniPlayerTrackGestures$ResetAnimator;-><init>()V
|
||||
|
||||
invoke-virtual {v1, v0}, Landroid/animation/ValueAnimator;->addUpdateListener(Landroid/animation/ValueAnimator$AnimatorUpdateListener;)V
|
||||
|
||||
sput-object v1, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||
|
||||
invoke-virtual {v1}, Landroid/animation/ValueAnimator;->start()V
|
||||
|
||||
return-void
|
||||
|
||||
:snap_zero
|
||||
invoke-static {v3}, Lradiant/MiniPlayerTrackGestures;->setDragOffsetDirect(F)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static beginDrag(FF)V
|
||||
.locals 2
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->resetGestureLock()V
|
||||
|
||||
sput p0, Lradiant/MiniPlayerTrackGestures;->e:F
|
||||
|
||||
sput p1, Lradiant/MiniPlayerTrackGestures;->f:F
|
||||
|
||||
const/4 v0, 0x1
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||
|
||||
if-eqz v0, :zero
|
||||
|
||||
invoke-virtual {v0}, Landroid/animation/ValueAnimator;->cancel()V
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||
|
||||
:zero
|
||||
const/4 v0, 0x0
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniPlayerTrackGestures;->setDragOffsetDirect(F)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static cancelDrag()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static clearResetAnimator(Landroid/animation/ValueAnimator;)V
|
||||
.locals 1
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||
|
||||
if-ne v0, p0, :done
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||
|
||||
:done
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static finishDrag(FF)I
|
||||
.locals 5
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->isAnyDragging()Z
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :check_active
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||
|
||||
return v0
|
||||
|
||||
:check_active
|
||||
|
||||
sget-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||
|
||||
if-nez v0, :active
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
return v0
|
||||
|
||||
:active
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||
|
||||
sget-boolean v1, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||
|
||||
sget v2, Lradiant/MiniPlayerTrackGestures;->e:F
|
||||
|
||||
sub-float/2addr p0, v2
|
||||
|
||||
sget v2, Lradiant/MiniPlayerTrackGestures;->f:F
|
||||
|
||||
sub-float/2addr p1, v2
|
||||
|
||||
if-eqz v1, :reset
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->suppressTapOpen()V
|
||||
|
||||
:reset
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||
|
||||
invoke-static {p0}, Ljava/lang/Math;->abs(F)F
|
||||
|
||||
move-result v1
|
||||
|
||||
const/high16 v2, 0x42400000 # 48.0f -> at least 48dp horizontal swipe
|
||||
|
||||
invoke-static {v2}, Lradiant/MiniPlayerTrackGestures;->dp(F)F
|
||||
|
||||
move-result v2
|
||||
|
||||
cmpg-float v3, v1, v2
|
||||
|
||||
if-gez v3, :check_axis
|
||||
|
||||
return v0
|
||||
|
||||
:check_axis
|
||||
invoke-static {p1}, Ljava/lang/Math;->abs(F)F
|
||||
|
||||
move-result p1
|
||||
|
||||
const/high16 v2, 0x3fc00000 # 1.5f -> horizontal distance must dominate vertical mov. by 1.5x
|
||||
|
||||
mul-float/2addr p1, v2
|
||||
|
||||
cmpg-float v1, v1, p1
|
||||
|
||||
if-lez v1, :no_swipe
|
||||
|
||||
const/4 p1, 0x0
|
||||
|
||||
cmpg-float p0, p0, p1
|
||||
|
||||
if-ltz p0, :next
|
||||
|
||||
const/4 v0, 0x1
|
||||
|
||||
return v0
|
||||
|
||||
:next
|
||||
const/4 v0, -0x1
|
||||
|
||||
return v0
|
||||
|
||||
:no_swipe
|
||||
return v0
|
||||
.end method
|
||||
|
||||
.method public static moveDrag(FF)V
|
||||
.locals 6
|
||||
|
||||
sget-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||
|
||||
if-eqz v0, :done
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->isAnyDragging()Z
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :move_continue
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||
|
||||
goto :done
|
||||
|
||||
:move_continue
|
||||
sget v0, Lradiant/MiniPlayerTrackGestures;->e:F
|
||||
|
||||
sub-float/2addr p0, v0
|
||||
|
||||
sget v0, Lradiant/MiniPlayerTrackGestures;->f:F
|
||||
|
||||
sub-float/2addr p1, v0
|
||||
|
||||
invoke-static {p0}, Ljava/lang/Math;->abs(F)F
|
||||
|
||||
move-result v0
|
||||
|
||||
invoke-static {p1}, Ljava/lang/Math;->abs(F)F
|
||||
|
||||
move-result v1
|
||||
|
||||
sget-boolean v2, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||
|
||||
if-nez v2, :publish
|
||||
|
||||
const/high16 v2, 0x40c00000 # 6.0f dp dead zone
|
||||
|
||||
invoke-static {v2}, Lradiant/MiniPlayerTrackGestures;->dp(F)F
|
||||
|
||||
move-result v2
|
||||
|
||||
cmpg-float v3, v0, v2
|
||||
|
||||
if-gez v3, :move_axis
|
||||
|
||||
goto :done
|
||||
|
||||
:move_axis
|
||||
const v2, 0x3f8ccccd # 1.1f horizontal dominance while dragging
|
||||
|
||||
mul-float/2addr v1, v2
|
||||
|
||||
cmpg-float v2, v0, v1
|
||||
|
||||
if-gez v2, :accept
|
||||
|
||||
goto :done
|
||||
|
||||
:accept
|
||||
invoke-static {}, Lradiant/MiniPlayerGestures;->lockHorizontalGesture()Z
|
||||
|
||||
move-result v2
|
||||
|
||||
if-eqz v2, :cancel_locked
|
||||
|
||||
const/4 v2, 0x1
|
||||
|
||||
sput-boolean v2, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||
|
||||
:publish
|
||||
const/4 v2, 0x0
|
||||
|
||||
invoke-static {v2}, Lradiant/MiniPlayerGestures;->setSwipeFeedbackDirect(F)V
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->suppressTapOpen()V
|
||||
|
||||
invoke-static {p0}, Lradiant/MiniPlayerTrackGestures;->setDragOffset(F)V
|
||||
|
||||
:done
|
||||
return-void
|
||||
|
||||
:cancel_locked
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->g:Z
|
||||
|
||||
sput-boolean v0, Lradiant/MiniPlayerTrackGestures;->h:Z
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||
|
||||
goto :done
|
||||
.end method
|
||||
|
||||
.method private static dp(F)F
|
||||
.locals 1
|
||||
|
||||
invoke-static {}, Landroid/content/res/Resources;->getSystem()Landroid/content/res/Resources;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-virtual {v0}, Landroid/content/res/Resources;->getDisplayMetrics()Landroid/util/DisplayMetrics;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
iget v0, v0, Landroid/util/DisplayMetrics;->density:F
|
||||
|
||||
mul-float/2addr p0, v0
|
||||
|
||||
return p0
|
||||
.end method
|
||||
|
||||
.method public static feedbackModifier(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
|
||||
.locals 2
|
||||
|
||||
const/high16 v0, 0x3f800000 # 1.0f
|
||||
|
||||
invoke-static {p0, v0}, Landroidx/compose/ui/ZIndexModifierKt;->zIndex(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier;
|
||||
|
||||
move-result-object p0
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures$OffsetLayer;->INSTANCE:Lradiant/MiniPlayerTrackGestures$OffsetLayer;
|
||||
|
||||
invoke-static {p0, v0}, Landroidx/compose/ui/graphics/GraphicsLayerModifierKt;->graphicsLayer(Landroidx/compose/ui/Modifier;Lam0/l;)Landroidx/compose/ui/Modifier;
|
||||
|
||||
move-result-object p0
|
||||
|
||||
return-object p0
|
||||
.end method
|
||||
|
||||
.method public static textFeedbackModifier(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
|
||||
.locals 1
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures$TextDraw;->INSTANCE:Lradiant/MiniPlayerTrackGestures$TextDraw; # text clip on right only
|
||||
|
||||
invoke-static {p0, v0}, Landroidx/compose/ui/draw/DrawModifierKt;->drawWithContent(Landroidx/compose/ui/Modifier;Lam0/l;)Landroidx/compose/ui/Modifier;
|
||||
|
||||
move-result-object p0
|
||||
|
||||
return-object p0
|
||||
.end method
|
||||
|
||||
.method public static onRenderedItem(Ljava/lang/Object;)V
|
||||
.locals 2
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures;->d:Ljava/lang/Object;
|
||||
|
||||
if-eqz v0, :store_initial
|
||||
|
||||
invoke-virtual {v0, p0}, Ljava/lang/Object;->equals(Ljava/lang/Object;)Z
|
||||
|
||||
move-result v1
|
||||
|
||||
if-nez v1, :done
|
||||
|
||||
sput-object p0, Lradiant/MiniPlayerTrackGestures;->d:Ljava/lang/Object;
|
||||
|
||||
invoke-static {}, Lradiant/MiniPlayerTrackGestures;->animateReset()V
|
||||
|
||||
return-void
|
||||
|
||||
:store_initial
|
||||
sput-object p0, Lradiant/MiniPlayerTrackGestures;->d:Ljava/lang/Object;
|
||||
|
||||
:done
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static consumeTapOpenSuppression()Z
|
||||
.locals 5
|
||||
|
||||
sget-wide v0, Lradiant/MiniPlayerTrackGestures;->a:J
|
||||
|
||||
const-wide/16 v2, 0x0
|
||||
|
||||
cmp-long v4, v0, v2
|
||||
|
||||
if-nez v4, :check_time
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
return v0
|
||||
|
||||
:check_time
|
||||
sput-wide v2, Lradiant/MiniPlayerTrackGestures;->a:J
|
||||
|
||||
invoke-static {}, Landroid/os/SystemClock;->uptimeMillis()J
|
||||
|
||||
move-result-wide v2
|
||||
|
||||
cmp-long v4, v2, v0
|
||||
|
||||
if-lez v4, :yes
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
return v0
|
||||
|
||||
:yes
|
||||
const/4 v0, 0x1
|
||||
|
||||
return v0
|
||||
.end method
|
||||
|
||||
.method public static suppressTapOpen()V
|
||||
.locals 4
|
||||
|
||||
invoke-static {}, Landroid/os/SystemClock;->uptimeMillis()J
|
||||
|
||||
move-result-wide v0
|
||||
|
||||
const-wide/16 v2, 0x258
|
||||
|
||||
add-long/2addr v0, v2
|
||||
|
||||
sput-wide v0, Lradiant/MiniPlayerTrackGestures;->a:J
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static setDragOffset(F)V
|
||||
.locals 5
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||
|
||||
if-eqz v0, :rubber_band
|
||||
|
||||
invoke-virtual {v0}, Landroid/animation/ValueAnimator;->cancel()V
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-object v0, Lradiant/MiniPlayerTrackGestures;->c:Landroid/animation/ValueAnimator;
|
||||
|
||||
:rubber_band
|
||||
invoke-static {p0}, Ljava/lang/Math;->abs(F)F
|
||||
|
||||
move-result v0
|
||||
|
||||
const/high16 v1, 0x42800000 # 64.0f dp free travel
|
||||
|
||||
invoke-static {v1}, Lradiant/MiniPlayerTrackGestures;->dp(F)F
|
||||
|
||||
move-result v1
|
||||
|
||||
cmpg-float v2, v0, v1
|
||||
|
||||
if-lez v2, :publish
|
||||
|
||||
sub-float/2addr v0, v1
|
||||
|
||||
const/high16 v2, 0x3e800000 # 0.25f resistance after 64dp
|
||||
|
||||
mul-float/2addr v0, v2
|
||||
|
||||
const v2, 0x3eb33333 # 0.35f max extra travel
|
||||
|
||||
mul-float/2addr v2, v1
|
||||
|
||||
invoke-static {v0, v2}, Ljava/lang/Math;->min(FF)F
|
||||
|
||||
move-result v0
|
||||
|
||||
add-float/2addr v0, v1
|
||||
|
||||
const/4 v1, 0x0
|
||||
|
||||
cmpg-float v2, p0, v1
|
||||
|
||||
if-gez v2, :positive
|
||||
|
||||
neg-float p0, v0
|
||||
|
||||
goto :publish
|
||||
|
||||
:positive
|
||||
move p0, v0
|
||||
|
||||
|
||||
:publish
|
||||
invoke-static {p0}, Lradiant/MiniPlayerTrackGestures;->setDragOffsetDirect(F)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static setDragOffsetDirect(F)V
|
||||
.locals 1
|
||||
|
||||
sget-object v0, Lradiant/MiniPlayerTrackGestures;->b:Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
invoke-interface {v0, p0}, Landroidx/compose/runtime/MutableFloatState;->setFloatValue(F)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static trackModifier(Landroidx/compose/ui/Modifier;Lam0/l;)Landroidx/compose/ui/Modifier;
|
||||
.locals 1
|
||||
|
||||
new-instance v0, Lradiant/MiniPlayerTrackGestures$Gesture;
|
||||
|
||||
invoke-direct {v0, p1}, Lradiant/MiniPlayerTrackGestures$Gesture;-><init>(Lam0/l;)V
|
||||
|
||||
invoke-static {p0, v0}, Landroidx/compose/ui/input/pointer/PointerInteropFilter_androidKt;->motionEventSpy(Landroidx/compose/ui/Modifier;Lam0/l;)Landroidx/compose/ui/Modifier;
|
||||
|
||||
move-result-object p0
|
||||
|
||||
return-object p0
|
||||
.end method
|
||||
@@ -0,0 +1,436 @@
|
||||
.class public final Lradiant/MiniSeekerFloating;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.implements Lam0/l;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static final INSTANCE:Lradiant/MiniSeekerFloating;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
new-instance v0, Lradiant/MiniSeekerFloating; # singleton
|
||||
|
||||
invoke-direct {v0}, Lradiant/MiniSeekerFloating;-><init>()V # ctor
|
||||
|
||||
sput-object v0, Lradiant/MiniSeekerFloating;->INSTANCE:Lradiant/MiniSeekerFloating; # publish
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public final invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
.locals 2
|
||||
|
||||
check-cast p1, Landroidx/compose/ui/graphics/drawscope/ContentDrawScope; # narrow type
|
||||
|
||||
invoke-interface {p1}, Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;->drawContent()V # draw pill content
|
||||
|
||||
invoke-static {}, Lradiant/MiniSeekerLine;->start()V # lazy subscribe
|
||||
|
||||
:try_start
|
||||
invoke-static {p1}, Lradiant/MiniSeekerFloating;->drawBorder(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V # draw progress
|
||||
:try_end
|
||||
.catch Ljava/lang/Throwable; {:try_start .. :try_end} :swallow
|
||||
|
||||
:swallow
|
||||
sget-object v0, Lkotlin/u;->a:Lkotlin/u; # Unit
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
|
||||
|
||||
.method private static drawBorder(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
|
||||
.locals 60
|
||||
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->progressState:Landroidx/compose/runtime/MutableFloatState; # state
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F # read tracked
|
||||
|
||||
move-result v0 # raw progress
|
||||
|
||||
const/4 v1, 0x0 # lower bound
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->max(FF)F # clamp low
|
||||
|
||||
move-result v0 # clamped low
|
||||
|
||||
const/high16 v1, 0x3f800000 # 1.0f upper
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->min(FF)F # clamp high
|
||||
|
||||
move-result v0 # final progress
|
||||
|
||||
const/4 v1, 0x0 # zero
|
||||
|
||||
cmpg-float v2, v0, v1 # compare to zero
|
||||
|
||||
if-lez v2, :done # skip if empty
|
||||
|
||||
move/from16 v16, v0 # stash progress
|
||||
|
||||
invoke-interface/range {p0 .. p0}, Landroidx/compose/ui/graphics/drawscope/DrawScope;->getSize-NH-jbRc()J # size long
|
||||
|
||||
move-result-wide v0 # size pair
|
||||
|
||||
invoke-static {v0, v1}, Landroidx/compose/ui/geometry/Size;->getWidth-impl(J)F # extract width
|
||||
|
||||
move-result v2 # width px
|
||||
|
||||
move/from16 v17, v2 # stash width
|
||||
|
||||
invoke-static {v0, v1}, Landroidx/compose/ui/geometry/Size;->getHeight-impl(J)F # extract height
|
||||
|
||||
move-result v2 # height px
|
||||
|
||||
move/from16 v18, v2 # stash height
|
||||
|
||||
invoke-interface/range {p0 .. p0}, Landroidx/compose/ui/unit/Density;->getDensity()F # density
|
||||
|
||||
move-result v2 # px per dp
|
||||
|
||||
move/from16 v19, v2 # stash density
|
||||
|
||||
const/high16 v0, 0x42100000 # 36.0f dp
|
||||
|
||||
move/from16 v1, v19 # density
|
||||
|
||||
mul-float v2, v0, v1 # to px
|
||||
|
||||
move/from16 v20, v2 # stash r
|
||||
|
||||
const/high16 v0, 0x40000000 # 2.0f
|
||||
|
||||
move/from16 v1, v20 # r
|
||||
|
||||
mul-float v2, v1, v0 # 2*r
|
||||
|
||||
move/from16 v21, v2 # stash 2r
|
||||
|
||||
move/from16 v0, v17 # w
|
||||
|
||||
move/from16 v1, v20 # r
|
||||
|
||||
sub-float v2, v0, v1 # w-r
|
||||
|
||||
move/from16 v22, v2 # stash w-r
|
||||
|
||||
move/from16 v0, v18 # h
|
||||
|
||||
move/from16 v1, v20 # r
|
||||
|
||||
sub-float v2, v0, v1 # h-r
|
||||
|
||||
move/from16 v23, v2 # stash h-r
|
||||
|
||||
move/from16 v0, v17 # w
|
||||
|
||||
move/from16 v1, v21 # 2r
|
||||
|
||||
sub-float v2, v0, v1 # w-2r
|
||||
|
||||
move/from16 v24, v2 # stash w-2r
|
||||
|
||||
move/from16 v0, v18 # h
|
||||
|
||||
move/from16 v1, v21 # 2r
|
||||
|
||||
sub-float v2, v0, v1 # h-2r
|
||||
|
||||
move/from16 v25, v2 # stash h-2r
|
||||
|
||||
move/from16 v0, v17 # w
|
||||
|
||||
const/high16 v1, 0x40000000 # 2.0f
|
||||
|
||||
div-float v2, v0, v1 # w/2
|
||||
|
||||
move/from16 v26, v2 # stash top-center x
|
||||
|
||||
const/high16 v0, 0x42b40000 # 90.0f
|
||||
|
||||
move/from16 v27, v0 # stash 90
|
||||
|
||||
const/4 v0, 0x0 # zero
|
||||
|
||||
move/from16 v1, v27 # 90
|
||||
|
||||
sub-float v2, v0, v1 # -90
|
||||
|
||||
move/from16 v28, v2 # stash -90
|
||||
|
||||
const/high16 v0, 0x43340000 # 180.0f
|
||||
|
||||
move/from16 v29, v0 # stash 180
|
||||
|
||||
invoke-static {}, Landroidx/compose/ui/graphics/AndroidPath_androidKt;->Path()Landroidx/compose/ui/graphics/Path; # new path
|
||||
|
||||
move-result-object v0 # path
|
||||
|
||||
move-object/from16 v30, v0 # stash path
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v26 # w/2
|
||||
|
||||
const/4 v2, 0x0 # 0
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->moveTo(FF)V # start top-center
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v22 # w-r
|
||||
|
||||
const/4 v2, 0x0 # 0
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # top edge right
|
||||
|
||||
new-instance v3, Landroidx/compose/ui/geometry/Rect; # arc bounds
|
||||
|
||||
move/from16 v4, v24 # w-2r
|
||||
|
||||
const/4 v5, 0x0 # 0
|
||||
|
||||
move/from16 v6, v17 # w
|
||||
|
||||
move/from16 v7, v21 # 2r
|
||||
|
||||
invoke-direct {v3, v4, v5, v6, v7}, Landroidx/compose/ui/geometry/Rect;-><init>(FFFF)V # TR rect
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v4, v28 # -90
|
||||
|
||||
move/from16 v5, v27 # 90
|
||||
|
||||
const/4 v6, 0x0 # false
|
||||
|
||||
invoke-interface {v0, v3, v4, v5, v6}, Landroidx/compose/ui/graphics/Path;->arcTo(Landroidx/compose/ui/geometry/Rect;FFZ)V # TR corner
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v17 # w
|
||||
|
||||
move/from16 v2, v23 # h-r
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # right edge
|
||||
|
||||
new-instance v3, Landroidx/compose/ui/geometry/Rect; # arc bounds
|
||||
|
||||
move/from16 v4, v24 # w-2r
|
||||
|
||||
move/from16 v5, v25 # h-2r
|
||||
|
||||
move/from16 v6, v17 # w
|
||||
|
||||
move/from16 v7, v18 # h
|
||||
|
||||
invoke-direct {v3, v4, v5, v6, v7}, Landroidx/compose/ui/geometry/Rect;-><init>(FFFF)V # BR rect
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
const/4 v4, 0x0 # 0 deg
|
||||
|
||||
move/from16 v5, v27 # 90
|
||||
|
||||
const/4 v6, 0x0 # false
|
||||
|
||||
invoke-interface {v0, v3, v4, v5, v6}, Landroidx/compose/ui/graphics/Path;->arcTo(Landroidx/compose/ui/geometry/Rect;FFZ)V # BR corner
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v20 # r
|
||||
|
||||
move/from16 v2, v18 # h
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # bottom edge
|
||||
|
||||
new-instance v3, Landroidx/compose/ui/geometry/Rect; # arc bounds
|
||||
|
||||
const/4 v4, 0x0 # 0
|
||||
|
||||
move/from16 v5, v25 # h-2r
|
||||
|
||||
move/from16 v6, v21 # 2r
|
||||
|
||||
move/from16 v7, v18 # h
|
||||
|
||||
invoke-direct {v3, v4, v5, v6, v7}, Landroidx/compose/ui/geometry/Rect;-><init>(FFFF)V # BL rect
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v4, v27 # 90
|
||||
|
||||
move/from16 v5, v27 # 90
|
||||
|
||||
const/4 v6, 0x0 # false
|
||||
|
||||
invoke-interface {v0, v3, v4, v5, v6}, Landroidx/compose/ui/graphics/Path;->arcTo(Landroidx/compose/ui/geometry/Rect;FFZ)V # BL corner
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
const/4 v1, 0x0 # 0
|
||||
|
||||
move/from16 v2, v20 # r
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # left edge
|
||||
|
||||
new-instance v3, Landroidx/compose/ui/geometry/Rect; # arc bounds
|
||||
|
||||
const/4 v4, 0x0 # 0
|
||||
|
||||
const/4 v5, 0x0 # 0
|
||||
|
||||
move/from16 v6, v21 # 2r
|
||||
|
||||
move/from16 v7, v21 # 2r
|
||||
|
||||
invoke-direct {v3, v4, v5, v6, v7}, Landroidx/compose/ui/geometry/Rect;-><init>(FFFF)V # TL rect
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v4, v29 # 180
|
||||
|
||||
move/from16 v5, v27 # 90
|
||||
|
||||
const/4 v6, 0x0 # false
|
||||
|
||||
invoke-interface {v0, v3, v4, v5, v6}, Landroidx/compose/ui/graphics/Path;->arcTo(Landroidx/compose/ui/geometry/Rect;FFZ)V # TL corner
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v26 # w/2
|
||||
|
||||
const/4 v2, 0x0 # 0
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # close to start
|
||||
|
||||
const/high16 v0, 0x40000000 # 2.0f
|
||||
|
||||
move/from16 v1, v24 # w-2r
|
||||
|
||||
mul-float v2, v1, v0 # 2*(w-2r)
|
||||
|
||||
move/from16 v1, v25 # h-2r
|
||||
|
||||
mul-float v3, v1, v0 # 2*(h-2r)
|
||||
|
||||
add-float v4, v2, v3 # straight edges
|
||||
|
||||
const v5, 0x40c90fdb # 2*pi
|
||||
|
||||
move/from16 v6, v20 # r
|
||||
|
||||
mul-float v7, v6, v5 # arc length
|
||||
|
||||
add-float v8, v4, v7 # total perim
|
||||
|
||||
move/from16 v31, v8 # stash perim
|
||||
|
||||
move/from16 v0, v16 # progress
|
||||
|
||||
move/from16 v1, v31 # perim
|
||||
|
||||
mul-float v2, v0, v1 # visible length
|
||||
|
||||
move/from16 v32, v2 # stash visible
|
||||
|
||||
sget-object v0, Landroidx/compose/ui/graphics/PathEffect;->Companion:Landroidx/compose/ui/graphics/PathEffect$Companion; # companion
|
||||
|
||||
const/4 v1, 0x2 # length 2
|
||||
|
||||
new-array v1, v1, [F # intervals
|
||||
|
||||
const/4 v2, 0x0 # index 0
|
||||
|
||||
move/from16 v3, v32 # visible
|
||||
|
||||
aput v3, v1, v2 # dash length
|
||||
|
||||
const/4 v2, 0x1 # index 1
|
||||
|
||||
move/from16 v3, v31 # perim
|
||||
|
||||
aput v3, v1, v2 # gap length
|
||||
|
||||
const/4 v2, 0x0 # phase int
|
||||
|
||||
int-to-float v2, v2 # phase float
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Landroidx/compose/ui/graphics/PathEffect$Companion;->dashPathEffect([FF)Landroidx/compose/ui/graphics/PathEffect; # dash effect
|
||||
|
||||
move-result-object v0 # effect
|
||||
|
||||
move-object/from16 v33, v0 # stash effect
|
||||
|
||||
const/high16 v0, 0x40000000 # 2.0f
|
||||
|
||||
move/from16 v1, v19 # density
|
||||
|
||||
mul-float v2, v0, v1 # stroke px
|
||||
|
||||
move/from16 v34, v2 # stash stroke width
|
||||
|
||||
new-instance v40, Landroidx/compose/ui/graphics/drawscope/Stroke; # stroke obj
|
||||
|
||||
move/from16 v41, v34 # width
|
||||
|
||||
const/16 v42, 0x0 # miter default
|
||||
|
||||
const/16 v43, 0x0 # cap default
|
||||
|
||||
const/16 v44, 0x0 # join default
|
||||
|
||||
move-object/from16 v45, v33 # effect
|
||||
|
||||
const/16 v46, 0xe # default mask
|
||||
|
||||
const/16 v47, 0x0 # marker null
|
||||
|
||||
invoke-direct/range {v40 .. v47}, Landroidx/compose/ui/graphics/drawscope/Stroke;-><init>(FFIILandroidx/compose/ui/graphics/PathEffect;ILkotlin/jvm/internal/DefaultConstructorMarker;)V # stroke ctor
|
||||
|
||||
move-object/from16 v35, v40 # stash stroke
|
||||
|
||||
const v0, -0x1 # white ARGB
|
||||
|
||||
invoke-static {v0}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack color
|
||||
|
||||
move-result-wide v0 # color long
|
||||
|
||||
move-wide/from16 v36, v0 # stash color
|
||||
|
||||
move-object/from16 v50, p0 # scope
|
||||
|
||||
move-object/from16 v51, v30 # path
|
||||
|
||||
move-wide/from16 v52, v36 # color
|
||||
|
||||
const/16 v54, 0x0 # alpha default
|
||||
|
||||
move-object/from16 v55, v35 # style
|
||||
|
||||
const/16 v56, 0x0 # filter null
|
||||
|
||||
const/16 v57, 0x0 # blend default
|
||||
|
||||
const/16 v58, 0x34 # default mask
|
||||
|
||||
const/16 v59, 0x0 # marker null
|
||||
|
||||
invoke-static/range {v50 .. v59}, Landroidx/compose/ui/graphics/drawscope/DrawScope;->drawPath-LG529CI$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;Landroidx/compose/ui/graphics/Path;JFLandroidx/compose/ui/graphics/drawscope/DrawStyle;Landroidx/compose/ui/graphics/ColorFilter;IILjava/lang/Object;)V # draw stroke
|
||||
|
||||
:done
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,200 @@
|
||||
.class public final Lradiant/MiniSeekerLine;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static progressState:Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
.field public static volatile vm:Lgh/j;
|
||||
|
||||
.field public static volatile sub:Lio/reactivex/disposables/Disposable;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0 # initial progress
|
||||
|
||||
invoke-static {v0}, Landroidx/compose/runtime/PrimitiveSnapshotStateKt;->mutableFloatStateOf(F)Landroidx/compose/runtime/MutableFloatState; # state holder
|
||||
|
||||
move-result-object v0 # state instance
|
||||
|
||||
sput-object v0, Lradiant/MiniSeekerLine;->progressState:Landroidx/compose/runtime/MutableFloatState; # publish state
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method private static dlog(Ljava/lang/String;)V
|
||||
.locals 1
|
||||
|
||||
const-string v0, "RLProgressLine" # log tag
|
||||
|
||||
invoke-static {v0, p0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I # info log
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public static start()V
|
||||
.locals 3
|
||||
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->sub:Lio/reactivex/disposables/Disposable; # current sub
|
||||
|
||||
if-eqz v0, :start_block # only run once
|
||||
|
||||
return-void
|
||||
|
||||
:start_block
|
||||
:try_start
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->vm:Lgh/j; # cached vm
|
||||
|
||||
if-nez v0, :got_vm # reuse if present
|
||||
|
||||
invoke-static {}, Lcom/aspiro/wamp/App$a;->a()Lcom/aspiro/wamp/App; # app singleton
|
||||
|
||||
move-result-object v0 # app
|
||||
|
||||
invoke-virtual {v0}, Lcom/aspiro/wamp/App;->e()Le5/c; # dagger component
|
||||
|
||||
move-result-object v0 # component
|
||||
|
||||
check-cast v0, Le5/z$n2; # narrow type
|
||||
|
||||
iget-object v0, v0, Le5/z$n2;->z0:Ldagger/internal/j; # playqueue provider
|
||||
|
||||
invoke-interface {v0}, Lql0/a;->get()Ljava/lang/Object; # resolve
|
||||
|
||||
move-result-object v0 # playqueue
|
||||
|
||||
check-cast v0, Lcom/aspiro/wamp/playqueue/d1; # narrow type
|
||||
|
||||
new-instance v1, Lgh/j; # build vm
|
||||
|
||||
invoke-direct {v1, v0}, Lgh/j;-><init>(Lcom/aspiro/wamp/playqueue/d1;)V # ctor
|
||||
|
||||
sput-object v1, Lradiant/MiniSeekerLine;->vm:Lgh/j; # cache
|
||||
|
||||
:got_vm
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->vm:Lgh/j; # vm ref
|
||||
|
||||
invoke-virtual {v0}, Lgh/j;->a()V # activate emission
|
||||
|
||||
iget-object v0, v0, Lgh/j;->d:Lio/reactivex/subjects/BehaviorSubject; # position subject
|
||||
|
||||
invoke-static {}, Lio/reactivex/android/schedulers/AndroidSchedulers;->mainThread()Lio/reactivex/Scheduler; # main scheduler
|
||||
|
||||
move-result-object v1 # scheduler
|
||||
|
||||
invoke-virtual {v0, v1}, Lio/reactivex/Observable;->observeOn(Lio/reactivex/Scheduler;)Lio/reactivex/Observable; # observe main
|
||||
|
||||
move-result-object v0 # observable
|
||||
|
||||
new-instance v1, Lradiant/SeekerConsumer; # consumer
|
||||
|
||||
invoke-direct {v1}, Lradiant/SeekerConsumer;-><init>()V # ctor
|
||||
|
||||
invoke-virtual {v0, v1}, Lio/reactivex/Observable;->subscribe(Lio/reactivex/functions/Consumer;)Lio/reactivex/disposables/Disposable; # subscribe
|
||||
|
||||
move-result-object v0 # disposable
|
||||
|
||||
sput-object v0, Lradiant/MiniSeekerLine;->sub:Lio/reactivex/disposables/Disposable; # cache
|
||||
|
||||
const-string v0, "started" # log msg
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniSeekerLine;->dlog(Ljava/lang/String;)V # info log
|
||||
|
||||
return-void
|
||||
:try_end
|
||||
.catch Ljava/lang/Throwable; {:try_start .. :try_end} :swallow
|
||||
|
||||
:swallow
|
||||
move-exception v0 # error
|
||||
|
||||
const-string v1, "start failed" # log msg
|
||||
|
||||
invoke-static {v1}, Lradiant/MiniSeekerLine;->dlog(Ljava/lang/String;)V # info log
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public static render(Landroidx/compose/runtime/Composer;I)V
|
||||
.locals 5
|
||||
.annotation build Landroidx/compose/runtime/Composable;
|
||||
.end annotation
|
||||
|
||||
invoke-static {}, Lradiant/MiniSeekerLine;->start()V # lazy subscribe
|
||||
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->progressState:Landroidx/compose/runtime/MutableFloatState; # state
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F # read tracked
|
||||
|
||||
move-result v0 # raw progress
|
||||
|
||||
const/4 v1, 0x0 # lower bound
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->max(FF)F # clamp low
|
||||
|
||||
move-result v0 # clamped low
|
||||
|
||||
const/high16 v1, 0x3f800000 # 1.0f upper
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->min(FF)F # clamp high
|
||||
|
||||
move-result v0 # final progress
|
||||
|
||||
const v1, 0x52414c49 # 'RALI' slot key
|
||||
|
||||
invoke-interface {p0, v1}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open group
|
||||
|
||||
sget-object v1, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion; # base modifier
|
||||
|
||||
invoke-static {v1, v0}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxWidth(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier; # width fraction
|
||||
|
||||
move-result-object v0 # filled modifier
|
||||
|
||||
const/4 v1, 0x2 # 2 dp
|
||||
|
||||
int-to-float v1, v1 # to float
|
||||
|
||||
invoke-static {v1}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F # box as Dp
|
||||
|
||||
move-result v1 # dp value
|
||||
|
||||
invoke-static {v0, v1}, Landroidx/compose/foundation/layout/SizeKt;->height-3ABfNKs(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier; # 2dp tall
|
||||
|
||||
move-result-object v0 # sized modifier
|
||||
|
||||
const v1, -0x1 # 0xFFFFFFFF white
|
||||
|
||||
invoke-static {v1}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack color
|
||||
|
||||
move-result-wide v1 # color long
|
||||
|
||||
invoke-static {}, Landroidx/compose/ui/graphics/RectangleShapeKt;->getRectangleShape()Landroidx/compose/ui/graphics/Shape; # rect shape
|
||||
|
||||
move-result-object v3 # shape
|
||||
|
||||
invoke-static {v0, v1, v2, v3}, Landroidx/compose/foundation/BackgroundKt;->background-bw27NRU(Landroidx/compose/ui/Modifier;JLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier; # white bg
|
||||
|
||||
move-result-object v0 # final modifier
|
||||
|
||||
const/4 v1, 0x0 # composer $changed
|
||||
|
||||
invoke-static {v0, p0, v1}, Landroidx/compose/foundation/layout/SpacerKt;->Spacer(Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V # draw line
|
||||
|
||||
invoke-interface {p0}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close group
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -1,6 +1,6 @@
|
||||
.class public final Lradiant/NoOp;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Lyl0/a;
|
||||
.implements Lam0/a;
|
||||
|
||||
|
||||
# static fields
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
.class public final Lradiant/PlayingFrom;
|
||||
.super Ljava/lang/Object;
|
||||
.source "PlayingFrom.smali"
|
||||
|
||||
|
||||
# static fields
|
||||
|
||||
# puts the "Playing from" source above track title
|
||||
.field public static a:Ljava/lang/String;
|
||||
@@ -0,0 +1,124 @@
|
||||
.class public final Lradiant/QualityBadge;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
# qualities : 0 = LOW / HIGH, 1 = LOSSLESS, 2 = HI_RES_LOSSLESS
|
||||
.method private static backgroundArgb(I)I
|
||||
.locals 0
|
||||
|
||||
packed-switch p0, :quality_switch
|
||||
|
||||
const p0, 0x66475569 # LOW / HIGH bg
|
||||
return p0
|
||||
|
||||
:lossless
|
||||
const p0, 0x3306b6d4 # LOSSLESS bg
|
||||
return p0
|
||||
|
||||
:hires
|
||||
const p0, 0x40f59e0b # HI_RES_LOSSLESS bg
|
||||
return p0
|
||||
|
||||
:quality_switch
|
||||
.packed-switch 0x1
|
||||
:lossless
|
||||
:hires
|
||||
.end packed-switch
|
||||
.end method
|
||||
|
||||
.method private static textArgb(I)I
|
||||
.locals 0
|
||||
|
||||
packed-switch p0, :quality_switch
|
||||
|
||||
const p0, -0x1d1710 # LOW / HIGH text
|
||||
return p0
|
||||
|
||||
:lossless
|
||||
const p0, -0x5a0c04 # LOSSLESS text
|
||||
return p0
|
||||
|
||||
:hires
|
||||
const p0, -0x21976 # HI_RES_LOSSLESS text
|
||||
return p0
|
||||
|
||||
:quality_switch
|
||||
.packed-switch 0x1
|
||||
:lossless
|
||||
:hires
|
||||
.end packed-switch
|
||||
.end method
|
||||
|
||||
|
||||
.method public static modifier(Landroidx/compose/ui/Modifier;I)Landroidx/compose/ui/Modifier;
|
||||
.locals 3
|
||||
|
||||
invoke-static {p1}, Lradiant/QualityBadge;->backgroundArgb(I)I
|
||||
|
||||
move-result p1
|
||||
invoke-static {p1}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J
|
||||
move-result-wide v0
|
||||
const/high16 p1, 0x40800000 # 4.0f
|
||||
invoke-static {p1}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F
|
||||
move-result p1
|
||||
invoke-static {p1}, Landroidx/compose/foundation/shape/RoundedCornerShapeKt;->RoundedCornerShape-0680j_4(F)Landroidx/compose/foundation/shape/RoundedCornerShape;
|
||||
move-result-object v2
|
||||
invoke-static {p0, v0, v1, v2}, Landroidx/compose/foundation/BackgroundKt;->background-bw27NRU(Landroidx/compose/ui/Modifier;JLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier;
|
||||
move-result-object p0
|
||||
const/high16 p1, 0x40c00000 # 6.0f
|
||||
invoke-static {p1}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F
|
||||
move-result p1
|
||||
const/high16 v0, 0x40000000 # 2.0f
|
||||
invoke-static {v0}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F
|
||||
move-result v0
|
||||
invoke-static {p0, p1, v0}, Landroidx/compose/foundation/layout/PaddingKt;->padding-VpY3zN4(Landroidx/compose/ui/Modifier;FF)Landroidx/compose/ui/Modifier;
|
||||
move-result-object p0
|
||||
return-object p0
|
||||
.end method
|
||||
|
||||
.method public static style(ILq20/c2;)Lq20/c2;
|
||||
.registers 16
|
||||
|
||||
invoke-static {p0}, Lradiant/QualityBadge;->textArgb(I)I
|
||||
|
||||
move-result v0
|
||||
new-instance v1, Lf20/a;
|
||||
invoke-direct {v1, v0}, Lf20/a;-><init>(I)V
|
||||
new-instance v0, Lf20/d;
|
||||
const/4 v2, 0x0
|
||||
const/4 v3, 0x0
|
||||
const/4 v4, 0x0
|
||||
const/4 v5, 0x0
|
||||
const/4 v6, 0x0
|
||||
const/4 v7, 0x0
|
||||
const/4 v8, 0x0
|
||||
const/4 v9, 0x0
|
||||
const/4 v10, 0x0
|
||||
const/4 v11, 0x0
|
||||
const/4 v12, 0x0
|
||||
const/16 v13, 0xffe
|
||||
const/4 v14, 0x0
|
||||
invoke-direct/range {v0 .. v14}, Lf20/d;-><init>(Lf20/a;Lf20/a;Lf20/a;Lf20/a;Lf20/a;Lf20/a;Lf20/a;Lf20/a;Lf20/a;Lf20/a;Lf20/a;Lf20/a;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
|
||||
move-object v9, v0
|
||||
move-object/from16 v0, p1
|
||||
const/4 v1, 0x0
|
||||
move-object v2, v9
|
||||
const/4 v3, 0x0
|
||||
const/4 v4, 0x0
|
||||
const/4 v5, 0x0
|
||||
const/16 v6, 0x1d
|
||||
const/4 v7, 0x0
|
||||
invoke-static/range {v0 .. v7}, Lq20/c2;->g(Lq20/c2;Lq20/s4;Lf20/d;Lcom/squareup/ui/market/core/text/MarketTextAlignment;Lcom/squareup/ui/market/core/text/MarketTextTransform;Lq20/r4;ILjava/lang/Object;)Lq20/c2;
|
||||
move-result-object p0
|
||||
return-object p0
|
||||
.end method
|
||||
@@ -0,0 +1,334 @@
|
||||
.class public final Lradiant/RLAPILyricsHook;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static volatile currentKey:Ljava/lang/String;
|
||||
|
||||
.field public static volatile currentVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
.field public static volatile isRlState:Z
|
||||
|
||||
.field public static volatile keepOpen:Z
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-object v0, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
|
||||
|
||||
sput-object v0, Lradiant/RLAPILyricsHook;->currentVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
sput-boolean v0, Lradiant/RLAPILyricsHook;->isRlState:Z
|
||||
|
||||
sput-boolean v0, Lradiant/RLAPILyricsHook;->keepOpen:Z
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public static dlog(Ljava/lang/String;)V
|
||||
.locals 1
|
||||
|
||||
const-string v0, "RLLyrics"
|
||||
|
||||
invoke-static {v0, p0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private static isBlank(Ljava/lang/String;)Z
|
||||
.locals 2
|
||||
|
||||
if-nez p0, :not_null
|
||||
|
||||
const/4 v0, 0x1
|
||||
|
||||
return v0
|
||||
|
||||
:not_null
|
||||
invoke-virtual {p0}, Ljava/lang/String;->trim()Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-virtual {v0}, Ljava/lang/String;->isEmpty()Z
|
||||
|
||||
move-result v1
|
||||
|
||||
return v1
|
||||
.end method
|
||||
|
||||
.method public static isOwner(Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;)Z
|
||||
.locals 2
|
||||
|
||||
sget-object v0, Lradiant/RLAPILyricsHook;->currentVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
if-ne p0, v0, :not_owner
|
||||
|
||||
sget-boolean v1, Lradiant/RLAPILyricsHook;->isRlState:Z
|
||||
|
||||
return v1
|
||||
|
||||
:not_owner
|
||||
const/4 v1, 0x0
|
||||
|
||||
return v1
|
||||
.end method
|
||||
|
||||
.method public static shouldKeepOpen(Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;)Z
|
||||
.locals 2
|
||||
|
||||
sget-object v0, Lradiant/RLAPILyricsHook;->currentVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
if-ne p0, v0, :do_not_keep
|
||||
|
||||
sget-boolean v1, Lradiant/RLAPILyricsHook;->keepOpen:Z
|
||||
|
||||
if-nez v1, :keep
|
||||
|
||||
sget-boolean v1, Lradiant/RLAPILyricsHook;->isRlState:Z
|
||||
|
||||
:keep
|
||||
return v1
|
||||
|
||||
:do_not_keep
|
||||
const/4 v1, 0x0
|
||||
|
||||
return v1
|
||||
.end method
|
||||
|
||||
.method public static onWampTrack(Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;Lcom/aspiro/wamp/model/Track;)V
|
||||
.locals 11
|
||||
|
||||
invoke-static {p1}, Lradiant/HomeBackdrop;->onTrack(Lcom/aspiro/wamp/model/Track;)V
|
||||
|
||||
const-string v3, "onWampTrack: hook entered"
|
||||
|
||||
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
if-eqz p1, :null_track
|
||||
|
||||
if-eqz p0, :done
|
||||
|
||||
goto :have_track
|
||||
|
||||
:null_track
|
||||
const-string v3, "onWampTrack: wamp Track is null, skip"
|
||||
|
||||
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
goto :invalid_track
|
||||
|
||||
:have_track
|
||||
invoke-virtual {p1}, Lcom/aspiro/wamp/model/MediaItem;->getTitle()Ljava/lang/String;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
if-nez v1, :title_present
|
||||
|
||||
const-string v3, "bail: getTitle() returned null"
|
||||
|
||||
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
goto :invalid_track
|
||||
|
||||
:title_present
|
||||
invoke-static {v1}, Lradiant/RLAPILyricsHook;->isBlank(Ljava/lang/String;)Z
|
||||
|
||||
move-result v2
|
||||
|
||||
if-eqz v2, :title_ok
|
||||
|
||||
const-string v3, "bail: title is blank"
|
||||
|
||||
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
goto :invalid_track
|
||||
|
||||
:title_ok
|
||||
# match desktop: primary artist name (artist?.name), fall back to the joined list
|
||||
invoke-virtual {p1}, Lcom/aspiro/wamp/model/MediaItem;->getArtist()Lcom/aspiro/wamp/model/Artist;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
if-eqz v3, :use_artist_names
|
||||
|
||||
invoke-virtual {v3}, Lcom/aspiro/wamp/model/Artist;->getName()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
if-nez v2, :artist_present
|
||||
|
||||
:use_artist_names
|
||||
invoke-virtual {p1}, Lcom/aspiro/wamp/model/MediaItem;->getArtistNames()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
if-nez v2, :artist_present
|
||||
|
||||
const-string v3, "bail: artist name null"
|
||||
|
||||
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
goto :invalid_track
|
||||
|
||||
:artist_present
|
||||
invoke-static {v2}, Lradiant/RLAPILyricsHook;->isBlank(Ljava/lang/String;)Z
|
||||
|
||||
move-result v3
|
||||
|
||||
if-eqz v3, :artist_ok
|
||||
|
||||
const-string v3, "bail: artist is blank"
|
||||
|
||||
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
goto :invalid_track
|
||||
|
||||
:artist_ok
|
||||
invoke-virtual {p1}, Lcom/aspiro/wamp/model/MediaItem;->getId()I
|
||||
|
||||
move-result v3
|
||||
|
||||
invoke-static {v3}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
|
||||
|
||||
move-result-object v4
|
||||
|
||||
sget-object v5, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
|
||||
|
||||
invoke-virtual {v4, v5}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
|
||||
|
||||
move-result v5
|
||||
|
||||
if-eqz v5, :rl_new_track
|
||||
|
||||
sget-object v5, Lradiant/RLAPILyricsHook;->currentVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
if-ne p0, v5, :rl_new_track
|
||||
|
||||
const-string v5, "onWampTrack: same track re-fired, skipping duplicate fetch"
|
||||
|
||||
invoke-static {v5}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
return-void
|
||||
|
||||
:rl_new_track
|
||||
iget-object v5, p0, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->P:Lkotlinx/coroutines/flow/MutableStateFlow;
|
||||
|
||||
invoke-interface {v5}, Lkotlinx/coroutines/flow/MutableStateFlow;->getValue()Ljava/lang/Object;
|
||||
|
||||
move-result-object v5
|
||||
|
||||
check-cast v5, Ljava/lang/Boolean;
|
||||
|
||||
invoke-virtual {v5}, Ljava/lang/Boolean;->booleanValue()Z
|
||||
|
||||
move-result v5
|
||||
|
||||
if-nez v5, :store_keep_open
|
||||
|
||||
sget-object v6, Lradiant/RLAPILyricsHook;->currentVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
if-eqz v6, :store_keep_open
|
||||
|
||||
if-eq p0, v6, :store_keep_open
|
||||
|
||||
iget-object v7, v6, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->P:Lkotlinx/coroutines/flow/MutableStateFlow;
|
||||
|
||||
invoke-interface {v7}, Lkotlinx/coroutines/flow/MutableStateFlow;->getValue()Ljava/lang/Object;
|
||||
|
||||
move-result-object v7
|
||||
|
||||
check-cast v7, Ljava/lang/Boolean;
|
||||
|
||||
invoke-virtual {v7}, Ljava/lang/Boolean;->booleanValue()Z
|
||||
|
||||
move-result v5
|
||||
|
||||
:store_keep_open
|
||||
sput-boolean v5, Lradiant/RLAPILyricsHook;->keepOpen:Z
|
||||
|
||||
const/4 v5, 0x0
|
||||
|
||||
sput-boolean v5, Lradiant/RLAPILyricsHook;->isRlState:Z
|
||||
|
||||
sput-object v4, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
|
||||
|
||||
sput-object p0, Lradiant/RLAPILyricsHook;->currentVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
move-object v3, v4
|
||||
|
||||
new-instance v5, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v6, "onWampTrack: fetching for title='"
|
||||
|
||||
invoke-direct {v5, v6}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v5, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v6, "' artist='"
|
||||
|
||||
invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v5, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v6, "'"
|
||||
|
||||
invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v5
|
||||
|
||||
invoke-static {v5}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
new-instance v5, Lradiant/RLAPILyricsWorker;
|
||||
|
||||
move-object v6, p0
|
||||
|
||||
move-object v7, v1
|
||||
|
||||
move-object v8, v2
|
||||
|
||||
move-object v9, v4
|
||||
|
||||
move-object v10, v3
|
||||
|
||||
invoke-direct/range {v5 .. v10}, Lradiant/RLAPILyricsWorker;-><init>(Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
|
||||
new-instance v6, Ljava/lang/Thread;
|
||||
|
||||
invoke-direct {v6, v5}, Ljava/lang/Thread;-><init>(Ljava/lang/Runnable;)V
|
||||
|
||||
const/4 v7, 0x1
|
||||
|
||||
invoke-virtual {v6, v7}, Ljava/lang/Thread;->setDaemon(Z)V
|
||||
|
||||
invoke-virtual {v6}, Ljava/lang/Thread;->start()V
|
||||
|
||||
goto :done
|
||||
|
||||
:invalid_track
|
||||
const/4 v3, 0x0
|
||||
|
||||
sput-boolean v3, Lradiant/RLAPILyricsHook;->isRlState:Z
|
||||
|
||||
sput-boolean v3, Lradiant/RLAPILyricsHook;->keepOpen:Z
|
||||
|
||||
sput-object v3, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
|
||||
|
||||
sput-object p0, Lradiant/RLAPILyricsHook;->currentVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
:done
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,893 @@
|
||||
.class public final Lradiant/RLAPILyricsWorker;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Ljava/lang/Runnable;
|
||||
|
||||
|
||||
# instance fields
|
||||
.field public final vm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
.field public final title:Ljava/lang/String;
|
||||
|
||||
.field public final artist:Ljava/lang/String;
|
||||
|
||||
.field public final key:Ljava/lang/String;
|
||||
|
||||
.field public final lyricsId:Ljava/lang/String;
|
||||
|
||||
.field public static volatile clientIp:Ljava/lang/String;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>(Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
iput-object p1, p0, Lradiant/RLAPILyricsWorker;->vm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
iput-object p2, p0, Lradiant/RLAPILyricsWorker;->title:Ljava/lang/String;
|
||||
|
||||
iput-object p3, p0, Lradiant/RLAPILyricsWorker;->artist:Ljava/lang/String;
|
||||
|
||||
iput-object p4, p0, Lradiant/RLAPILyricsWorker;->key:Ljava/lang/String;
|
||||
|
||||
iput-object p5, p0, Lradiant/RLAPILyricsWorker;->lyricsId:Ljava/lang/String;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private failCurrent()V
|
||||
.locals 4
|
||||
|
||||
invoke-direct {p0}, Lradiant/RLAPILyricsWorker;->isCurrent()Z
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :done
|
||||
|
||||
invoke-static {}, Lradiant/WordLyrics;->clear()V
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-object v0, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
|
||||
|
||||
sput-boolean v0, Lradiant/RLAPILyricsHook;->isRlState:Z
|
||||
|
||||
sget-boolean v0, Lradiant/StickyLyrics;->enabled:Z
|
||||
|
||||
if-nez v0, :done
|
||||
|
||||
sget-boolean v0, Lradiant/RLAPILyricsHook;->keepOpen:Z
|
||||
|
||||
if-nez v0, :done
|
||||
|
||||
iget-object v1, p0, Lradiant/RLAPILyricsWorker;->vm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
iget-object v2, v1, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->P:Lkotlinx/coroutines/flow/MutableStateFlow;
|
||||
|
||||
sget-object v3, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;
|
||||
|
||||
invoke-interface {v2, v3}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
|
||||
|
||||
:done
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private isCurrent()Z
|
||||
.locals 4
|
||||
|
||||
iget-object v0, p0, Lradiant/RLAPILyricsWorker;->vm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
sget-object v1, Lradiant/RLAPILyricsHook;->currentVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
if-ne v0, v1, :not_current
|
||||
|
||||
iget-object v1, v0, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->a:Lbi/e;
|
||||
|
||||
invoke-interface {v1}, Lbi/e;->getCurrentItem()Lcom/aspiro/wamp/playqueue/u0;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
if-eqz v1, :not_current
|
||||
|
||||
invoke-interface {v1}, Lcom/aspiro/wamp/playqueue/u0;->getMediaItem()Lcom/aspiro/wamp/model/MediaItem;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
if-eqz v1, :not_current
|
||||
|
||||
invoke-virtual {v1}, Lcom/aspiro/wamp/model/MediaItem;->getId()I
|
||||
|
||||
move-result v2
|
||||
|
||||
invoke-static {v2}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
iget-object v2, p0, Lradiant/RLAPILyricsWorker;->key:Ljava/lang/String;
|
||||
|
||||
invoke-virtual {v2, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
|
||||
|
||||
move-result v3
|
||||
|
||||
return v3
|
||||
|
||||
:not_current
|
||||
const/4 v3, 0x0
|
||||
|
||||
return v3
|
||||
.end method
|
||||
|
||||
# Public client IP (needed for debugging with ratelimits)
|
||||
.method static clientIp()Ljava/lang/String;
|
||||
.locals 6
|
||||
|
||||
sget-object v0, Lradiant/RLAPILyricsWorker;->clientIp:Ljava/lang/String;
|
||||
|
||||
if-eqz v0, :fetch
|
||||
|
||||
return-object v0
|
||||
|
||||
:fetch
|
||||
:try_start
|
||||
new-instance v0, Ljava/net/URL;
|
||||
|
||||
const-string v1, "https://api.ipify.org?format=text"
|
||||
|
||||
invoke-direct {v0, v1}, Ljava/net/URL;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v0}, Ljava/net/URL;->openConnection()Ljava/net/URLConnection;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
check-cast v0, Ljava/net/HttpURLConnection;
|
||||
|
||||
const/16 v1, 0x5dc
|
||||
|
||||
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setConnectTimeout(I)V
|
||||
|
||||
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setReadTimeout(I)V
|
||||
|
||||
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->getResponseCode()I
|
||||
|
||||
move-result v1
|
||||
|
||||
const/16 v2, 0xc8
|
||||
|
||||
if-eq v1, v2, :ok
|
||||
|
||||
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->disconnect()V
|
||||
|
||||
goto :fail
|
||||
|
||||
:ok
|
||||
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->getInputStream()Ljava/io/InputStream;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
new-instance v2, Ljava/io/InputStreamReader;
|
||||
|
||||
const-string v3, "UTF-8"
|
||||
|
||||
invoke-direct {v2, v1, v3}, Ljava/io/InputStreamReader;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
|
||||
|
||||
new-instance v3, Ljava/io/BufferedReader;
|
||||
|
||||
invoke-direct {v3, v2}, Ljava/io/BufferedReader;-><init>(Ljava/io/Reader;)V
|
||||
|
||||
invoke-virtual {v3}, Ljava/io/BufferedReader;->readLine()Ljava/lang/String;
|
||||
|
||||
move-result-object v4
|
||||
|
||||
invoke-virtual {v3}, Ljava/io/BufferedReader;->close()V
|
||||
|
||||
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->disconnect()V
|
||||
|
||||
if-eqz v4, :fail
|
||||
|
||||
invoke-virtual {v4}, Ljava/lang/String;->trim()Ljava/lang/String;
|
||||
|
||||
move-result-object v4
|
||||
|
||||
invoke-virtual {v4}, Ljava/lang/String;->isEmpty()Z
|
||||
|
||||
move-result v5
|
||||
|
||||
if-nez v5, :fail
|
||||
|
||||
sput-object v4, Lradiant/RLAPILyricsWorker;->clientIp:Ljava/lang/String;
|
||||
|
||||
return-object v4
|
||||
|
||||
:try_end
|
||||
.catchall {:try_start .. :try_end} :catch_all
|
||||
|
||||
:catch_all
|
||||
move-exception v0
|
||||
|
||||
:fail
|
||||
const-string v0, "null"
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
|
||||
.method public static fetch(Ljava/lang/String;Z)Ljava/lang/String;
|
||||
.locals 7
|
||||
|
||||
:try_start
|
||||
new-instance v0, Ljava/net/URL;
|
||||
|
||||
invoke-direct {v0, p0}, Ljava/net/URL;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v0}, Ljava/net/URL;->openConnection()Ljava/net/URLConnection;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
check-cast v0, Ljava/net/HttpURLConnection;
|
||||
|
||||
const-string v1, "GET"
|
||||
|
||||
invoke-virtual {v0, v1}, Ljava/net/HttpURLConnection;->setRequestMethod(Ljava/lang/String;)V
|
||||
|
||||
const v1, 0x2710
|
||||
|
||||
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setConnectTimeout(I)V
|
||||
|
||||
const v1, 0xafc8
|
||||
|
||||
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setReadTimeout(I)V
|
||||
|
||||
if-eqz p1, :no_auth
|
||||
|
||||
const-string v1, "P-Access-Token-Id"
|
||||
|
||||
const-string v2, "58hy4s86"
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Ljava/net/URLConnection;->setRequestProperty(Ljava/lang/String;Ljava/lang/String;)V
|
||||
|
||||
const-string v1, "P-Access-Token"
|
||||
|
||||
const-string v2, "xjehy2lfg5h5mjwotoxrcqugam"
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Ljava/net/URLConnection;->setRequestProperty(Ljava/lang/String;Ljava/lang/String;)V
|
||||
|
||||
const-string v1, "x-client-ip"
|
||||
|
||||
invoke-static {}, Lradiant/RLAPILyricsWorker;->clientIp()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Ljava/net/URLConnection;->setRequestProperty(Ljava/lang/String;Ljava/lang/String;)V
|
||||
|
||||
:no_auth
|
||||
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->getResponseCode()I
|
||||
|
||||
move-result v1
|
||||
|
||||
new-instance v3, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v4, "fetch status="
|
||||
|
||||
invoke-direct {v3, v4}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v3, v1}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v4, " url="
|
||||
|
||||
invoke-virtual {v3, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v3, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
const/16 v2, 0xc8
|
||||
|
||||
if-eq v1, v2, :status_ok
|
||||
|
||||
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->disconnect()V
|
||||
|
||||
const/4 v1, 0x0
|
||||
|
||||
return-object v1
|
||||
|
||||
:status_ok
|
||||
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->getInputStream()Ljava/io/InputStream;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
new-instance v2, Ljava/io/InputStreamReader;
|
||||
|
||||
const-string v3, "UTF-8"
|
||||
|
||||
invoke-direct {v2, v1, v3}, Ljava/io/InputStreamReader;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
|
||||
|
||||
new-instance v3, Ljava/io/BufferedReader;
|
||||
|
||||
invoke-direct {v3, v2}, Ljava/io/BufferedReader;-><init>(Ljava/io/Reader;)V
|
||||
|
||||
new-instance v4, Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-direct {v4}, Ljava/lang/StringBuilder;-><init>()V
|
||||
|
||||
:read_loop
|
||||
invoke-virtual {v3}, Ljava/io/BufferedReader;->readLine()Ljava/lang/String;
|
||||
|
||||
move-result-object v5
|
||||
|
||||
if-eqz v5, :read_done
|
||||
|
||||
invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
const/16 v5, 0xa
|
||||
|
||||
invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;
|
||||
|
||||
goto :read_loop
|
||||
|
||||
:read_done
|
||||
invoke-virtual {v3}, Ljava/io/BufferedReader;->close()V
|
||||
|
||||
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->disconnect()V
|
||||
|
||||
invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v6
|
||||
|
||||
return-object v6
|
||||
|
||||
:try_end
|
||||
.catchall {:try_start .. :try_end} :catch_all
|
||||
|
||||
:catch_all
|
||||
move-exception v0
|
||||
|
||||
new-instance v1, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v2, "fetch exception url="
|
||||
|
||||
invoke-direct {v1, v2}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v2, " err="
|
||||
|
||||
invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v0}, Ljava/lang/Throwable;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-virtual {v1, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
invoke-static {v1}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
const/4 v1, 0x0
|
||||
|
||||
return-object v1
|
||||
.end method
|
||||
|
||||
.method public static parseLines(Ljava/lang/String;)Ljava/util/ArrayList;
|
||||
.locals 11
|
||||
|
||||
:try_start
|
||||
new-instance v0, Lorg/json/JSONObject;
|
||||
|
||||
invoke-direct {v0, p0}, Lorg/json/JSONObject;-><init>(Ljava/lang/String;)V
|
||||
|
||||
const-string v1, "type"
|
||||
|
||||
const-string v2, ""
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Lorg/json/JSONObject;->optString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
const-string v2, "Line"
|
||||
|
||||
invoke-virtual {v2, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
|
||||
|
||||
move-result v2
|
||||
|
||||
if-nez v2, :type_ok
|
||||
|
||||
const-string v2, "Word"
|
||||
|
||||
invoke-virtual {v2, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
|
||||
|
||||
move-result v2
|
||||
|
||||
if-nez v2, :type_ok
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
return-object v0
|
||||
|
||||
:type_ok
|
||||
const-string v1, "data"
|
||||
|
||||
invoke-virtual {v0, v1}, Lorg/json/JSONObject;->optJSONArray(Ljava/lang/String;)Lorg/json/JSONArray;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
if-nez v0, :data_ok
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
return-object v0
|
||||
|
||||
:data_ok
|
||||
invoke-virtual {v0}, Lorg/json/JSONArray;->length()I
|
||||
|
||||
move-result v1
|
||||
|
||||
if-nez v1, :nonempty
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
return-object v0
|
||||
|
||||
:nonempty
|
||||
new-instance v2, Ljava/util/ArrayList;
|
||||
|
||||
invoke-direct {v2, v1}, Ljava/util/ArrayList;-><init>(I)V
|
||||
|
||||
const/4 v3, 0x0
|
||||
|
||||
:loop
|
||||
if-ge v3, v1, :loop_done
|
||||
|
||||
invoke-virtual {v0, v3}, Lorg/json/JSONArray;->getJSONObject(I)Lorg/json/JSONObject;
|
||||
|
||||
move-result-object v4
|
||||
|
||||
invoke-static {v4}, Lradiant/WordLyrics;->lineText(Lorg/json/JSONObject;)Ljava/lang/String;
|
||||
|
||||
move-result-object v5
|
||||
|
||||
const-string v6, "startTime"
|
||||
|
||||
const-wide/16 v7, 0x0
|
||||
|
||||
invoke-virtual {v4, v6, v7, v8}, Lorg/json/JSONObject;->optDouble(Ljava/lang/String;D)D
|
||||
|
||||
move-result-wide v7
|
||||
|
||||
const-wide v9, 0x408f400000000000L
|
||||
|
||||
mul-double/2addr v7, v9
|
||||
|
||||
double-to-long v7, v7
|
||||
|
||||
new-instance v4, Lcom/tidal/android/feature/playerscreen/ui/f;
|
||||
|
||||
invoke-direct {v4, v5, v7, v8}, Lcom/tidal/android/feature/playerscreen/ui/f;-><init>(Ljava/lang/String;J)V
|
||||
|
||||
invoke-virtual {v2, v4}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z
|
||||
|
||||
add-int/lit8 v3, v3, 0x1
|
||||
|
||||
goto :loop
|
||||
|
||||
:loop_done
|
||||
return-object v2
|
||||
|
||||
:try_end
|
||||
.catchall {:try_start .. :try_end} :catch_all
|
||||
|
||||
:catch_all
|
||||
move-exception v0
|
||||
|
||||
const/4 v1, 0x0
|
||||
|
||||
return-object v1
|
||||
.end method
|
||||
|
||||
# Returns "&isrc=<enc>" for the current track
|
||||
.method private isrcParam()Ljava/lang/String;
|
||||
.locals 6
|
||||
|
||||
iget-object v0, p0, Lradiant/RLAPILyricsWorker;->key:Ljava/lang/String;
|
||||
|
||||
const/4 v1, 0x0
|
||||
|
||||
:loop
|
||||
invoke-static {v0}, Lradiant/IsrcCache;->get(Ljava/lang/String;)Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
if-eqz v2, :retry
|
||||
|
||||
invoke-virtual {v2}, Ljava/lang/String;->trim()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
invoke-virtual {v2}, Ljava/lang/String;->isEmpty()Z
|
||||
|
||||
move-result v3
|
||||
|
||||
if-nez v3, :retry
|
||||
|
||||
:try_enc_start
|
||||
invoke-static {v2}, Lradiant/RLAPILyricsWorker;->uric(Ljava/lang/String;)Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
:try_enc_end
|
||||
.catchall {:try_enc_start .. :try_enc_end} :enc_fail
|
||||
|
||||
:enc_done
|
||||
new-instance v3, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v4, "&isrc="
|
||||
|
||||
invoke-direct {v3, v4}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v3, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
return-object v2
|
||||
|
||||
:enc_fail
|
||||
move-exception v3
|
||||
|
||||
goto :enc_done
|
||||
|
||||
:retry
|
||||
# keep waiting for TIDAL's mapper to cache the ISRC while this track is still current
|
||||
invoke-direct {p0}, Lradiant/RLAPILyricsWorker;->isCurrent()Z
|
||||
|
||||
move-result v3
|
||||
|
||||
if-eqz v3, :giveup
|
||||
|
||||
const/16 v3, 0x28
|
||||
|
||||
if-ge v1, v3, :giveup
|
||||
|
||||
:try_sleep_start
|
||||
const-wide/16 v3, 0x64
|
||||
|
||||
invoke-static {v3, v4}, Ljava/lang/Thread;->sleep(J)V
|
||||
|
||||
:try_sleep_end
|
||||
.catchall {:try_sleep_start .. :try_sleep_end} :sleep_catch
|
||||
|
||||
:sleep_done
|
||||
add-int/lit8 v1, v1, 0x1
|
||||
|
||||
goto :loop
|
||||
|
||||
:sleep_catch
|
||||
move-exception v3
|
||||
|
||||
goto :sleep_done
|
||||
|
||||
:giveup
|
||||
const-string v0, ""
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
|
||||
# URL-encode like JS encodeURIComponent (so requests are byte identical to the desktop plugin)
|
||||
.method static uric(Ljava/lang/String;)Ljava/lang/String;
|
||||
.locals 3
|
||||
|
||||
const-string v0, "UTF-8"
|
||||
|
||||
invoke-static {p0, v0}, Ljava/net/URLEncoder;->encode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
const-string v1, "+"
|
||||
|
||||
const-string v2, "%20"
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
const-string v1, "%21"
|
||||
|
||||
const-string v2, "!"
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
const-string v1, "%27"
|
||||
|
||||
const-string v2, "\'"
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
const-string v1, "%28"
|
||||
|
||||
const-string v2, "("
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
const-string v1, "%29"
|
||||
|
||||
const-string v2, ")"
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
const-string v1, "%7E"
|
||||
|
||||
const-string v2, "~"
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
|
||||
.method private runImpl()V
|
||||
.locals 13
|
||||
|
||||
iget-object v0, p0, Lradiant/RLAPILyricsWorker;->title:Ljava/lang/String;
|
||||
|
||||
invoke-static {v0}, Lradiant/RLAPILyricsWorker;->uric(Ljava/lang/String;)Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
iget-object v2, p0, Lradiant/RLAPILyricsWorker;->artist:Ljava/lang/String;
|
||||
|
||||
invoke-static {v2}, Lradiant/RLAPILyricsWorker;->uric(Ljava/lang/String;)Ljava/lang/String;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
new-instance v2, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v3, "?title="
|
||||
|
||||
invoke-direct {v2, v3}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v0, "&artist="
|
||||
|
||||
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v2, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-direct {p0}, Lradiant/RLAPILyricsWorker;->isrcParam()Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-static {}, Lradiant/WordLyrics;->romanizeOn()Z
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :no_romanize
|
||||
|
||||
const-string v0, "&romanize=true"
|
||||
|
||||
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
:no_romanize
|
||||
const-string v0, "&platform=rl-mobile"
|
||||
|
||||
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
new-instance v3, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v4, "https://api.atomix.one/rl-api"
|
||||
|
||||
invoke-direct {v3, v4}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v3, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
const/4 v4, 0x1
|
||||
|
||||
invoke-static {v3, v4}, Lradiant/RLAPILyricsWorker;->fetch(Ljava/lang/String;Z)Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
if-nez v3, :got_body
|
||||
|
||||
new-instance v3, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v4, "https://rl-api.kineticsand.net/lyrics"
|
||||
|
||||
invoke-direct {v3, v4}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v3, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
const/4 v4, 0x0
|
||||
|
||||
invoke-static {v3, v4}, Lradiant/RLAPILyricsWorker;->fetch(Ljava/lang/String;Z)Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
if-nez v3, :got_body
|
||||
|
||||
const-string v4, "both primary and fallback fetches failed"
|
||||
|
||||
invoke-static {v4}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
goto :request_failed
|
||||
|
||||
:got_body
|
||||
move-object v11, v3
|
||||
|
||||
invoke-static {v3}, Lradiant/RLAPILyricsWorker;->parseLines(Ljava/lang/String;)Ljava/util/ArrayList;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
if-nez v3, :parse_ok
|
||||
|
||||
const-string v4, "parse returned null (bad JSON / unsupported type)"
|
||||
|
||||
invoke-static {v4}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
goto :request_failed
|
||||
|
||||
:parse_ok
|
||||
invoke-virtual {v3}, Ljava/util/ArrayList;->size()I
|
||||
|
||||
move-result v4
|
||||
|
||||
if-nez v4, :nonempty
|
||||
|
||||
const-string v5, "parsed 0 lines"
|
||||
|
||||
invoke-static {v5}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
goto :request_failed
|
||||
|
||||
:nonempty
|
||||
new-instance v5, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v6, "parsed "
|
||||
|
||||
invoke-direct {v5, v6}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v5, v4}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v6, " lines"
|
||||
|
||||
invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v5
|
||||
|
||||
invoke-static {v5}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
invoke-direct {p0}, Lradiant/RLAPILyricsWorker;->isCurrent()Z
|
||||
|
||||
move-result v4
|
||||
|
||||
if-nez v4, :key_ok
|
||||
|
||||
const-string v5, "race-check failed (user skipped tracks)"
|
||||
|
||||
invoke-static {v5}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
return-void
|
||||
|
||||
:key_ok
|
||||
invoke-static {v11}, Lradiant/WordLyrics;->ingest(Ljava/lang/String;)V
|
||||
|
||||
iget-object v4, p0, Lradiant/RLAPILyricsWorker;->vm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
iget-object v5, v4, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->O:Lkotlinx/coroutines/flow/MutableStateFlow;
|
||||
|
||||
invoke-static {v3}, Lvn0/a;->c(Ljava/lang/Iterable;)Lvn0/b;
|
||||
|
||||
move-result-object v6
|
||||
|
||||
iget-object v7, p0, Lradiant/RLAPILyricsWorker;->lyricsId:Ljava/lang/String;
|
||||
|
||||
if-nez v7, :have_id
|
||||
|
||||
const-string v7, ""
|
||||
|
||||
:have_id
|
||||
new-instance v8, Lcom/tidal/android/feature/playerscreen/ui/g$c;
|
||||
|
||||
const/4 v9, -0x1
|
||||
|
||||
const/4 v10, 0x0
|
||||
|
||||
invoke-direct {v8, v7, v6, v9, v10}, Lcom/tidal/android/feature/playerscreen/ui/g$c;-><init>(Ljava/lang/String;Lvn0/b;IZ)V
|
||||
|
||||
const-string v6, "publishing g$c -> K=true (O=true if sticky) N=g$c"
|
||||
|
||||
invoke-static {v6}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
const/4 v6, 0x1
|
||||
|
||||
invoke-interface {v5, v8}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
|
||||
|
||||
sput-boolean v6, Lradiant/RLAPILyricsHook;->isRlState:Z
|
||||
|
||||
iget-object v6, v4, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->K:Lkotlinx/coroutines/flow/MutableStateFlow;
|
||||
|
||||
sget-object v7, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;
|
||||
|
||||
invoke-interface {v6, v7}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
|
||||
|
||||
sget-boolean v9, Lradiant/RLAPILyricsHook;->keepOpen:Z
|
||||
|
||||
if-nez v9, :open_lyrics
|
||||
|
||||
sget-boolean v9, Lradiant/StickyLyrics;->enabled:Z
|
||||
|
||||
if-eqz v9, :skip_n
|
||||
|
||||
:open_lyrics
|
||||
|
||||
iget-object v6, v4, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->P:Lkotlinx/coroutines/flow/MutableStateFlow;
|
||||
|
||||
invoke-interface {v6, v7}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
|
||||
|
||||
:skip_n
|
||||
:done
|
||||
return-void
|
||||
|
||||
:request_failed
|
||||
invoke-direct {p0}, Lradiant/RLAPILyricsWorker;->failCurrent()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public run()V
|
||||
.locals 2
|
||||
|
||||
:try_start
|
||||
invoke-direct {p0}, Lradiant/RLAPILyricsWorker;->runImpl()V
|
||||
|
||||
:try_end
|
||||
.catchall {:try_start .. :try_end} :catch_all
|
||||
|
||||
return-void
|
||||
|
||||
:catch_all
|
||||
move-exception v0
|
||||
|
||||
invoke-virtual {v0}, Ljava/lang/Throwable;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
invoke-static {v1}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
invoke-direct {p0}, Lradiant/RLAPILyricsWorker;->failCurrent()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,60 @@
|
||||
.class public final Lradiant/SeekerConsumer;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.implements Lio/reactivex/functions/Consumer;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final accept(Ljava/lang/Object;)V
|
||||
.locals 3
|
||||
|
||||
if-eqz p1, :ret # null guard
|
||||
|
||||
instance-of v0, p1, Lgh/b; # type guard
|
||||
|
||||
if-eqz v0, :ret # skip if wrong type
|
||||
|
||||
check-cast p1, Lgh/b; # narrow type
|
||||
|
||||
iget v0, p1, Lgh/b;->b:F # raw progress
|
||||
|
||||
invoke-static {v0}, Ljava/lang/Float;->isNaN(F)Z # nan check
|
||||
|
||||
move-result v1 # nan flag
|
||||
|
||||
if-eqz v1, :not_nan # skip clamp if finite
|
||||
|
||||
const/4 v0, 0x0 # default to zero
|
||||
|
||||
:not_nan
|
||||
const/4 v1, 0x0 # lower bound
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->max(FF)F # clamp low
|
||||
|
||||
move-result v0 # clamped low
|
||||
|
||||
const/high16 v2, 0x3f800000 # 1.0f upper bound
|
||||
|
||||
invoke-static {v0, v2}, Ljava/lang/Math;->min(FF)F # clamp high
|
||||
|
||||
move-result v0 # final value
|
||||
|
||||
sget-object v1, Lradiant/MiniSeekerLine;->progressState:Landroidx/compose/runtime/MutableFloatState; # shared state
|
||||
|
||||
if-eqz v1, :ret # safety
|
||||
|
||||
invoke-interface {v1, v0}, Landroidx/compose/runtime/MutableFloatState;->setFloatValue(F)V # publish
|
||||
|
||||
:ret
|
||||
return-void
|
||||
.end method
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public static final a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lyl0/a;)V
|
||||
.method public static final a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lam0/a;)V
|
||||
.locals 21
|
||||
.annotation build Landroidx/compose/runtime/Composable;
|
||||
.end annotation
|
||||
@@ -98,7 +98,7 @@
|
||||
|
||||
const/4 v4, 0x6
|
||||
|
||||
invoke-static {v2, v11, v4}, Lcom/squareup/ui/market/core/theme/x;->t(Lcom/squareup/ui/market/core/theme/k$a;Landroidx/compose/runtime/Composer;I)Lcom/squareup/ui/market/core/theme/MarketStylesheet;
|
||||
invoke-static {v2, v11, v4}, Lcom/squareup/ui/market/core/theme/z;->t(Lcom/squareup/ui/market/core/theme/k$a;Landroidx/compose/runtime/Composer;I)Lcom/squareup/ui/market/core/theme/MarketStylesheet;
|
||||
|
||||
move-result-object v15
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
|
||||
const/16 v18, 0x0
|
||||
|
||||
invoke-static/range {v15 .. v20}, Lcom/squareup/ui/market/components/MarketIconButtonKt;->P(Lcom/squareup/ui/market/core/theme/MarketStylesheet;Lcom/squareup/ui/market/core/components/properties/IconButton$Size;Lcom/squareup/ui/market/core/components/properties/IconButton$Rank;Lcom/squareup/ui/market/core/components/properties/IconButton$Variant;ILjava/lang/Object;)Ln20/w1;
|
||||
invoke-static/range {v15 .. v20}, Lcom/squareup/ui/market/components/MarketIconButtonKt;->P(Lcom/squareup/ui/market/core/theme/MarketStylesheet;Lcom/squareup/ui/market/core/components/properties/IconButton$Size;Lcom/squareup/ui/market/core/components/properties/IconButton$Rank;Lcom/squareup/ui/market/core/components/properties/IconButton$Variant;ILjava/lang/Object;)Lq20/u1;
|
||||
|
||||
move-result-object v4
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
:cond_5
|
||||
move-object v9, v4
|
||||
|
||||
check-cast v9, Ln20/w1;
|
||||
check-cast v9, Lq20/u1;
|
||||
|
||||
sget v2, Lcom/tidal/android/feature/playerscreen/ui/R$string;->lyrics:I
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
|
||||
const/4 v8, 0x0
|
||||
|
||||
invoke-static/range {v1 .. v13}, Lcom/squareup/ui/market/components/MarketIconButtonKt;->c(Lyl0/a;Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/interaction/MutableInteractionSource;ZLcom/squareup/ui/market/components/n;Lyl0/a;Ljava/lang/String;Ln20/w1;Lyl0/p;Landroidx/compose/runtime/Composer;II)V
|
||||
invoke-static/range {v1 .. v13}, Lcom/squareup/ui/market/components/MarketIconButtonKt;->b(Lam0/a;Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/interaction/MutableInteractionSource;ZLcom/squareup/ui/market/components/n;Lam0/a;Ljava/lang/String;Lq20/u1;Lam0/p;Landroidx/compose/runtime/Composer;II)V
|
||||
|
||||
invoke-static {}, Landroidx/compose/runtime/ComposerKt;->isTraceInProgress()Z
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
.source "SourceFile"
|
||||
|
||||
# interfaces
|
||||
.implements Lyl0/p;
|
||||
.implements Lam0/p;
|
||||
|
||||
|
||||
# virtual methods
|
||||
@@ -35,7 +35,7 @@
|
||||
invoke-static {v0, p2, v1, v2}, Landroidx/compose/runtime/ComposerKt;->traceEventStart(IIILjava/lang/String;)V
|
||||
|
||||
:cond_0
|
||||
const p2, 0x7f08051d
|
||||
const p2, 0x7f080522
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.class public final Lradiant/SpvFactory;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Lyl0/l;
|
||||
.implements Lam0/l;
|
||||
|
||||
|
||||
# static fields
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
.class public final Lradiant/StickyLyrics;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static volatile enabled:Z
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-boolean v0, Lradiant/StickyLyrics;->enabled:Z
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,59 @@
|
||||
.class public final Lradiant/SylBrush;
|
||||
.super Landroidx/compose/ui/graphics/ShaderBrush;
|
||||
|
||||
# Paints the syllable wipe gradient
|
||||
# x0 = wipe edge, x1 = end of the soft trail
|
||||
|
||||
|
||||
# instance state
|
||||
|
||||
.field public final x0:F
|
||||
|
||||
.field public final x1:F
|
||||
|
||||
.field public final c0:I
|
||||
|
||||
.field public final c1:I
|
||||
|
||||
|
||||
.method public constructor <init>(FFII)V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Landroidx/compose/ui/graphics/ShaderBrush;-><init>()V
|
||||
|
||||
iput p1, p0, Lradiant/SylBrush;->x0:F
|
||||
|
||||
iput p2, p0, Lradiant/SylBrush;->x1:F
|
||||
|
||||
iput p3, p0, Lradiant/SylBrush;->c0:I
|
||||
|
||||
iput p4, p0, Lradiant/SylBrush;->c1:I
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# Gradient across the whole line
|
||||
.method public createShader-uvyYCjk(J)Landroid/graphics/Shader;
|
||||
.locals 8
|
||||
|
||||
new-instance v0, Landroid/graphics/LinearGradient;
|
||||
|
||||
iget v1, p0, Lradiant/SylBrush;->x0:F
|
||||
|
||||
const/4 v2, 0x0
|
||||
|
||||
iget v3, p0, Lradiant/SylBrush;->x1:F
|
||||
|
||||
const/4 v4, 0x0
|
||||
|
||||
iget v5, p0, Lradiant/SylBrush;->c0:I
|
||||
|
||||
iget v6, p0, Lradiant/SylBrush;->c1:I
|
||||
|
||||
sget-object v7, Landroid/graphics/Shader$TileMode;->CLAMP:Landroid/graphics/Shader$TileMode;
|
||||
|
||||
invoke-direct/range {v0 .. v7}, Landroid/graphics/LinearGradient;-><init>(FFFFIILandroid/graphics/Shader$TileMode;)V
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
@@ -0,0 +1,264 @@
|
||||
.class public final Lradiant/SylLayout;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
# interfaces
|
||||
.implements Lam0/l;
|
||||
|
||||
# Remembers where each line's glyphs landed
|
||||
|
||||
|
||||
# static state
|
||||
|
||||
.field public static cbs:[Lradiant/SylLayout;
|
||||
|
||||
.field public static layouts:[Landroidx/compose/ui/text/TextLayoutResult;
|
||||
|
||||
|
||||
# instance state
|
||||
|
||||
.field public final idx:I
|
||||
|
||||
|
||||
.method public constructor <init>(I)V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
iput p1, p0, Lradiant/SylLayout;->idx:I
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# One reusable callback per line
|
||||
.method public static cb(I)Lam0/l;
|
||||
.locals 5
|
||||
|
||||
if-gez p0, :have_idx
|
||||
|
||||
const/4 p0, 0x0
|
||||
|
||||
:have_idx
|
||||
sget-object v0, Lradiant/SylLayout;->cbs:[Lradiant/SylLayout;
|
||||
|
||||
if-eqz v0, :grow
|
||||
|
||||
array-length v1, v0
|
||||
|
||||
if-gt v1, p0, :have_arr
|
||||
|
||||
:grow
|
||||
add-int/lit8 v1, p0, 0x1
|
||||
|
||||
const/16 v2, 0x40
|
||||
|
||||
if-ge v1, v2, :cap_ok
|
||||
|
||||
const/16 v1, 0x40
|
||||
|
||||
:cap_ok
|
||||
if-nez v0, :copy
|
||||
|
||||
new-array v0, v1, [Lradiant/SylLayout;
|
||||
|
||||
goto :store_arr
|
||||
|
||||
:copy
|
||||
invoke-static {v0, v1}, Ljava/util/Arrays;->copyOf([Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
check-cast v0, [Lradiant/SylLayout;
|
||||
|
||||
:store_arr
|
||||
sput-object v0, Lradiant/SylLayout;->cbs:[Lradiant/SylLayout;
|
||||
|
||||
:have_arr
|
||||
aget-object v1, v0, p0
|
||||
|
||||
if-nez v1, :ret
|
||||
|
||||
new-instance v1, Lradiant/SylLayout;
|
||||
|
||||
invoke-direct {v1, p0}, Lradiant/SylLayout;-><init>(I)V
|
||||
|
||||
aput-object v1, v0, p0
|
||||
|
||||
:ret
|
||||
return-object v1
|
||||
.end method
|
||||
|
||||
.method public static get(I)Landroidx/compose/ui/text/TextLayoutResult;
|
||||
.locals 2
|
||||
|
||||
if-ltz p0, :none
|
||||
|
||||
sget-object v0, Lradiant/SylLayout;->layouts:[Landroidx/compose/ui/text/TextLayoutResult;
|
||||
|
||||
if-eqz v0, :none
|
||||
|
||||
array-length v1, v0
|
||||
|
||||
if-ge p0, v1, :none
|
||||
|
||||
aget-object v0, v0, p0
|
||||
|
||||
return-object v0
|
||||
|
||||
:none
|
||||
const/4 v0, 0x0
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
|
||||
# Forget the layouts on track change
|
||||
.method public static reset()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-object v0, Lradiant/SylLayout;->layouts:[Landroidx/compose/ui/text/TextLayoutResult;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
# Font size in pixels
|
||||
.method public static em(Landroidx/compose/ui/text/TextLayoutResult;)F
|
||||
.locals 8
|
||||
|
||||
invoke-virtual {p0}, Landroidx/compose/ui/text/TextLayoutResult;->getLayoutInput()Landroidx/compose/ui/text/TextLayoutInput;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-virtual {v0}, Landroidx/compose/ui/text/TextLayoutInput;->getStyle()Landroidx/compose/ui/text/TextStyle;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
invoke-virtual {v1}, Landroidx/compose/ui/text/TextStyle;->getFontSize-XSAIIZE()J
|
||||
|
||||
move-result-wide v1
|
||||
|
||||
# Only Sp converts to pixels
|
||||
invoke-static {v1, v2}, Landroidx/compose/ui/unit/TextUnit;->getType-UIouoOA(J)J
|
||||
|
||||
move-result-wide v3
|
||||
|
||||
sget-object v5, Landroidx/compose/ui/unit/TextUnitType;->Companion:Landroidx/compose/ui/unit/TextUnitType$Companion;
|
||||
|
||||
invoke-virtual {v5}, Landroidx/compose/ui/unit/TextUnitType$Companion;->getSp-UIouoOA()J
|
||||
|
||||
move-result-wide v5
|
||||
|
||||
invoke-static {v3, v4, v5, v6}, Landroidx/compose/ui/unit/TextUnitType;->equals-impl0(JJ)Z
|
||||
|
||||
move-result v3
|
||||
|
||||
if-eqz v3, :fallback
|
||||
|
||||
invoke-virtual {v0}, Landroidx/compose/ui/text/TextLayoutInput;->getDensity()Landroidx/compose/ui/unit/Density;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/unit/Density;->toPx--R2X_6o(J)F
|
||||
|
||||
move-result v0
|
||||
|
||||
const/4 v1, 0x0
|
||||
|
||||
cmpg-float v1, v0, v1
|
||||
|
||||
if-lez v1, :fallback
|
||||
|
||||
return v0
|
||||
|
||||
:fallback
|
||||
# No font size, guess from the line box
|
||||
const/4 v0, 0x0
|
||||
|
||||
invoke-virtual {p0, v0}, Landroidx/compose/ui/text/TextLayoutResult;->getLineBottom(I)F
|
||||
|
||||
move-result v1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
invoke-virtual {p0, v0}, Landroidx/compose/ui/text/TextLayoutResult;->getLineTop(I)F
|
||||
|
||||
move-result v2
|
||||
|
||||
sub-float/2addr v1, v2
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
cmpg-float v0, v1, v0
|
||||
|
||||
if-lez v0, :default_em
|
||||
|
||||
const v0, 0x3f4ccccd # 0.8f
|
||||
|
||||
mul-float/2addr v0, v1
|
||||
|
||||
return v0
|
||||
|
||||
:default_em
|
||||
const/high16 v0, 0x42200000 # 40.0f
|
||||
|
||||
return v0
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
|
||||
.method public invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
.locals 4
|
||||
|
||||
iget v0, p0, Lradiant/SylLayout;->idx:I
|
||||
|
||||
if-ltz v0, :ret
|
||||
|
||||
instance-of v1, p1, Landroidx/compose/ui/text/TextLayoutResult;
|
||||
|
||||
if-eqz v1, :ret
|
||||
|
||||
sget-object v1, Lradiant/SylLayout;->layouts:[Landroidx/compose/ui/text/TextLayoutResult;
|
||||
|
||||
if-eqz v1, :grow
|
||||
|
||||
array-length v2, v1
|
||||
|
||||
if-gt v2, v0, :store
|
||||
|
||||
:grow
|
||||
add-int/lit8 v2, v0, 0x1
|
||||
|
||||
const/16 v3, 0x40
|
||||
|
||||
if-ge v2, v3, :cap_ok
|
||||
|
||||
const/16 v2, 0x40
|
||||
|
||||
:cap_ok
|
||||
if-nez v1, :copy
|
||||
|
||||
new-array v1, v2, [Landroidx/compose/ui/text/TextLayoutResult;
|
||||
|
||||
goto :store
|
||||
|
||||
:copy
|
||||
invoke-static {v1, v2}, Ljava/util/Arrays;->copyOf([Ljava/lang/Object;I)[Ljava/lang/Object;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
check-cast v1, [Landroidx/compose/ui/text/TextLayoutResult;
|
||||
|
||||
:store
|
||||
sput-object v1, Lradiant/SylLayout;->layouts:[Landroidx/compose/ui/text/TextLayoutResult;
|
||||
|
||||
check-cast p1, Landroidx/compose/ui/text/TextLayoutResult;
|
||||
|
||||
aput-object p1, v1, v0
|
||||
|
||||
:ret
|
||||
sget-object v0, Lkotlin/u;->a:Lkotlin/u;
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
@@ -0,0 +1,87 @@
|
||||
.class public final Lradiant/WazeInitReceiver;
|
||||
.super Landroid/content/BroadcastReceiver;
|
||||
.source "WazeInitReceiver.smali"
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Landroid/content/BroadcastReceiver;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public onReceive(Landroid/content/Context;Landroid/content/Intent;)V
|
||||
.locals 5
|
||||
|
||||
if-eqz p1, :return
|
||||
|
||||
if-eqz p2, :return
|
||||
|
||||
const-string v0, "com.waze.sdk.audio.ACTION_INIT"
|
||||
|
||||
invoke-virtual {p2}, Landroid/content/Intent;->getAction()Ljava/lang/String;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
|
||||
|
||||
move-result v0
|
||||
|
||||
if-eqz v0, :return
|
||||
|
||||
const-string v0, "token"
|
||||
|
||||
invoke-virtual {p2, v0}, Landroid/content/Intent;->getStringExtra(Ljava/lang/String;)Ljava/lang/String;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
if-eqz v0, :return
|
||||
|
||||
invoke-virtual {v0}, Ljava/lang/String;->length()I
|
||||
|
||||
move-result v1
|
||||
|
||||
if-lez v1, :return
|
||||
|
||||
invoke-virtual {p1}, Landroid/content/Context;->getApplicationContext()Landroid/content/Context;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
if-eqz v1, :context_ready
|
||||
|
||||
move-object p1, v1
|
||||
|
||||
:context_ready
|
||||
new-instance v1, Landroid/content/Intent;
|
||||
|
||||
invoke-direct {v1}, Landroid/content/Intent;-><init>()V
|
||||
|
||||
const-string v2, "com.waze"
|
||||
|
||||
const-string v3, "com.waze.sdk.SdkService"
|
||||
|
||||
invoke-virtual {v1, v2, v3}, Landroid/content/Intent;->setClassName(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;
|
||||
|
||||
new-instance v2, Lradiant/WazeServiceConnection;
|
||||
|
||||
invoke-direct {v2, p1, v0}, Lradiant/WazeServiceConnection;-><init>(Landroid/content/Context;Ljava/lang/String;)V
|
||||
|
||||
:try_start_0
|
||||
const/4 v3, 0x1
|
||||
|
||||
invoke-virtual {p1, v1, v2, v3}, Landroid/content/Context;->bindService(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z
|
||||
:try_end_0
|
||||
.catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0
|
||||
|
||||
goto :return
|
||||
|
||||
:catch_0
|
||||
move-exception v4
|
||||
|
||||
:return
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,228 @@
|
||||
.class public final Lradiant/WazeServiceConnection;
|
||||
.super Ljava/lang/Object;
|
||||
.source "WazeServiceConnection.smali"
|
||||
|
||||
# interfaces
|
||||
.implements Landroid/content/ServiceConnection;
|
||||
|
||||
|
||||
# instance fields
|
||||
.field private final context:Landroid/content/Context;
|
||||
|
||||
.field private final token:Ljava/lang/String;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>(Landroid/content/Context;Ljava/lang/String;)V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
iput-object p1, p0, Lradiant/WazeServiceConnection;->context:Landroid/content/Context;
|
||||
|
||||
iput-object p2, p0, Lradiant/WazeServiceConnection;->token:Ljava/lang/String;
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private addOpenMeIntent(Landroid/os/Bundle;)V
|
||||
.locals 4
|
||||
|
||||
iget-object v0, p0, Lradiant/WazeServiceConnection;->context:Landroid/content/Context;
|
||||
|
||||
invoke-virtual {v0}, Landroid/content/Context;->getPackageManager()Landroid/content/pm/PackageManager;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
invoke-virtual {v0}, Landroid/content/Context;->getPackageName()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
invoke-virtual {v1, v2}, Landroid/content/pm/PackageManager;->getLaunchIntentForPackage(Ljava/lang/String;)Landroid/content/Intent;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
if-eqz v1, :return
|
||||
|
||||
const/high16 v2, 0x10000000
|
||||
|
||||
invoke-virtual {v1, v2}, Landroid/content/Intent;->addFlags(I)Landroid/content/Intent;
|
||||
|
||||
const v2, 0xc000000
|
||||
|
||||
const/4 v3, 0x0
|
||||
|
||||
invoke-static {v0, v3, v1, v2}, Landroid/app/PendingIntent;->getActivity(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
const-string v2, "openMeIntent"
|
||||
|
||||
invoke-virtual {p1, v2, v1}, Landroid/os/Bundle;->putParcelable(Ljava/lang/String;Landroid/os/Parcelable;)V
|
||||
|
||||
:return
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private createClientMessenger()Landroid/os/Messenger;
|
||||
.locals 3
|
||||
|
||||
new-instance v0, Landroid/os/Handler;
|
||||
|
||||
invoke-static {}, Landroid/os/Looper;->getMainLooper()Landroid/os/Looper;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
invoke-direct {v0, v1}, Landroid/os/Handler;-><init>(Landroid/os/Looper;)V
|
||||
|
||||
new-instance v2, Landroid/os/Messenger;
|
||||
|
||||
invoke-direct {v2, v0}, Landroid/os/Messenger;-><init>(Landroid/os/Handler;)V
|
||||
|
||||
return-object v2
|
||||
.end method
|
||||
|
||||
.method private createConfigBundle()Landroid/os/Bundle;
|
||||
.locals 3
|
||||
|
||||
new-instance v0, Landroid/os/Bundle;
|
||||
|
||||
invoke-direct {v0}, Landroid/os/Bundle;-><init>()V
|
||||
|
||||
const-string v1, "versionCode"
|
||||
|
||||
const/16 v2, 0xa
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V
|
||||
|
||||
const-string v1, "themeColor"
|
||||
|
||||
const v2, __RL_WAZE_THEME_COLOR__ # Waze SDK themeColor
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Landroid/os/Bundle;->putInt(Ljava/lang/String;I)V
|
||||
|
||||
invoke-direct {p0, v0}, Lradiant/WazeServiceConnection;->addOpenMeIntent(Landroid/os/Bundle;)V
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
|
||||
.method private register(Landroid/os/IBinder;)V
|
||||
.locals 5
|
||||
|
||||
if-eqz p1, :return
|
||||
|
||||
invoke-static {}, Landroid/os/Parcel;->obtain()Landroid/os/Parcel;
|
||||
|
||||
move-result-object v0
|
||||
|
||||
invoke-static {}, Landroid/os/Parcel;->obtain()Landroid/os/Parcel;
|
||||
|
||||
move-result-object v1
|
||||
|
||||
const-string v2, "com.waze.sdk.ISdkService"
|
||||
|
||||
invoke-virtual {v0, v2}, Landroid/os/Parcel;->writeInterfaceToken(Ljava/lang/String;)V
|
||||
|
||||
iget-object v2, p0, Lradiant/WazeServiceConnection;->token:Ljava/lang/String;
|
||||
|
||||
invoke-virtual {v0, v2}, Landroid/os/Parcel;->writeString(Ljava/lang/String;)V
|
||||
|
||||
invoke-direct {p0}, Lradiant/WazeServiceConnection;->createConfigBundle()Landroid/os/Bundle;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
invoke-static {v0, v2}, Lradiant/WazeServiceConnection;->writeParcelable(Landroid/os/Parcel;Landroid/os/Parcelable;)V
|
||||
|
||||
invoke-direct {p0}, Lradiant/WazeServiceConnection;->createClientMessenger()Landroid/os/Messenger;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
invoke-static {v0, v2}, Lradiant/WazeServiceConnection;->writeParcelable(Landroid/os/Parcel;Landroid/os/Parcelable;)V
|
||||
|
||||
const/4 v2, 0x1
|
||||
|
||||
const/4 v3, 0x0
|
||||
|
||||
invoke-interface {p1, v2, v0, v1, v3}, Landroid/os/IBinder;->transact(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
|
||||
|
||||
move-result v4
|
||||
|
||||
if-eqz v4, :cleanup
|
||||
|
||||
invoke-virtual {v1}, Landroid/os/Parcel;->readException()V
|
||||
|
||||
:cleanup
|
||||
invoke-virtual {v1}, Landroid/os/Parcel;->recycle()V
|
||||
|
||||
invoke-virtual {v0}, Landroid/os/Parcel;->recycle()V
|
||||
|
||||
:return
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private safeUnbind()V
|
||||
.locals 1
|
||||
|
||||
:try_start_0
|
||||
iget-object v0, p0, Lradiant/WazeServiceConnection;->context:Landroid/content/Context;
|
||||
|
||||
invoke-virtual {v0, p0}, Landroid/content/Context;->unbindService(Landroid/content/ServiceConnection;)V
|
||||
:try_end_0
|
||||
.catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0
|
||||
|
||||
goto :return
|
||||
|
||||
:catch_0
|
||||
move-exception v0
|
||||
|
||||
:return
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private static writeParcelable(Landroid/os/Parcel;Landroid/os/Parcelable;)V
|
||||
.locals 2
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
if-eqz p1, :null_value
|
||||
|
||||
const/4 v1, 0x1
|
||||
|
||||
invoke-virtual {p0, v1}, Landroid/os/Parcel;->writeInt(I)V
|
||||
|
||||
invoke-interface {p1, p0, v0}, Landroid/os/Parcelable;->writeToParcel(Landroid/os/Parcel;I)V
|
||||
|
||||
return-void
|
||||
|
||||
:null_value
|
||||
invoke-virtual {p0, v0}, Landroid/os/Parcel;->writeInt(I)V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public onServiceConnected(Landroid/content/ComponentName;Landroid/os/IBinder;)V
|
||||
.locals 1
|
||||
|
||||
:try_start_0
|
||||
invoke-direct {p0, p2}, Lradiant/WazeServiceConnection;->register(Landroid/os/IBinder;)V
|
||||
:try_end_0
|
||||
.catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0
|
||||
|
||||
goto :unbind
|
||||
|
||||
:catch_0
|
||||
move-exception v0
|
||||
|
||||
:unbind
|
||||
invoke-direct {p0}, Lradiant/WazeServiceConnection;->safeUnbind()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public onServiceDisconnected(Landroid/content/ComponentName;)V
|
||||
.locals 0
|
||||
|
||||
return-void
|
||||
.end method
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
.class public final Lradiant/WordLyricsTick;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Ljava/lang/Runnable;
|
||||
|
||||
# 50ms clock tick driver for word level lyrics highlighting
|
||||
|
||||
.method public constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public run()V
|
||||
.locals 0
|
||||
|
||||
invoke-static {}, Lradiant/WordLyrics;->tick()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,44 @@
|
||||
--- a/com/tidal/android/feature/home/ui/k.smali
|
||||
+++ b/com/tidal/android/feature/home/ui/k.smali
|
||||
@@ -424,7 +424,7 @@
|
||||
const/4 v6, 0x0
|
||||
|
||||
.line 181
|
||||
- move-wide v7, v8
|
||||
+ const-wide/16 v7, 0x0 # transparent containerColor
|
||||
|
||||
.line 182
|
||||
const-wide/16 v9, 0x0
|
||||
--- a/com/tidal/android/feature/home/ui/o.smali
|
||||
+++ b/com/tidal/android/feature/home/ui/o.smali
|
||||
@@ -187,6 +187,10 @@
|
||||
.line 61
|
||||
.line 62
|
||||
:cond_3
|
||||
+ const/4 v0, 0x0 # render $changed flag
|
||||
+
|
||||
+ invoke-static {v4, v0}, Lradiant/HomeBackdrop;->render(Landroidx/compose/runtime/Composer;I)V # render album-art backdrop
|
||||
+
|
||||
invoke-interface {p1}, Landroidx/compose/foundation/layout/PaddingValues;->calculateTopPadding-D9Ej5fM()F
|
||||
|
||||
.line 63
|
||||
--- a/com/tidal/android/feature/home/ui/composables/g.smali
|
||||
+++ b/com/tidal/android/feature/home/ui/composables/g.smali
|
||||
@@ -790,6 +790,8 @@
|
||||
.line 104
|
||||
move-result-wide v14
|
||||
|
||||
+ const-wide/16 v14, 0x0 # transparent topBar background
|
||||
+
|
||||
.line 105
|
||||
const/16 v17, 0x2
|
||||
|
||||
@@ -850,6 +852,8 @@
|
||||
.line 134
|
||||
move-result-wide v14
|
||||
|
||||
+ const-wide/16 v14, 0x0 # transparent TopAppBar containerColor
|
||||
+
|
||||
.line 135
|
||||
sget-object v3, Landroidx/compose/ui/graphics/Color;->Companion:Landroidx/compose/ui/graphics/Color$Companion;
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
--- a/mb/f.smali
|
||||
+++ b/mb/f.smali
|
||||
@@ -613,6 +613,25 @@
|
||||
|
||||
.line 236
|
||||
:goto_8
|
||||
+ const-string/jumbo v5, "com.waze"
|
||||
+
|
||||
+ invoke-static {v3, v5}, Lkotlin/jvm/internal/n;->b(Ljava/lang/Object;Ljava/lang/Object;)Z # package match
|
||||
+
|
||||
+ move-result v5
|
||||
+
|
||||
+ if-eqz v5, :rl_waze_done
|
||||
+
|
||||
+ const-string/jumbo v5, "03:63:7f:6c:5d:8f:60:4e:6f:db:79:a6:ff:bf:a5:78:de:4e:31:8f:8d:a2:2f:c6:10:66:65:24:7f:88:07:d7" # Waze cert
|
||||
+
|
||||
+ invoke-static {v2, v5}, Lkotlin/jvm/internal/n;->b(Ljava/lang/Object;Ljava/lang/Object;)Z # cert match
|
||||
+
|
||||
+ move-result v5
|
||||
+
|
||||
+ if-eqz v5, :rl_waze_done
|
||||
+
|
||||
+ goto :goto_9 # trust Waze
|
||||
+
|
||||
+ :rl_waze_done
|
||||
invoke-static {}, Landroid/os/Process;->myUid()I
|
||||
|
||||
.line 237
|
||||
@@ -769,6 +788,26 @@
|
||||
:goto_b
|
||||
if-eqz v2, :cond_14
|
||||
|
||||
+ const-string/jumbo v1, "com.waze"
|
||||
+
|
||||
+ invoke-static {v3, v1}, Lkotlin/jvm/internal/n;->b(Ljava/lang/Object;Ljava/lang/Object;)Z # package match
|
||||
+
|
||||
+ move-result v1
|
||||
+
|
||||
+ if-eqz v1, :rl_waze_recent_root_done_2
|
||||
+
|
||||
+ new-instance v1, Landroidx/media/MediaBrowserServiceCompat$BrowserRoot;
|
||||
+
|
||||
+ const-string/jumbo v4, "__RL_WAZE_ROOT_ID__"
|
||||
+
|
||||
+ const/4 v5, 0x0
|
||||
+
|
||||
+ invoke-direct {v1, v4, v5}, Landroidx/media/MediaBrowserServiceCompat$BrowserRoot;-><init>(Ljava/lang/String;Landroid/os/Bundle;)V
|
||||
+
|
||||
+ return-object v1
|
||||
+
|
||||
+ :rl_waze_recent_root_done_2
|
||||
+
|
||||
.line 306
|
||||
.line 307
|
||||
iget-object v1, v0, Lmb/f;->c:Ljava/util/Map;
|
||||
@@ -1,23 +1,23 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/h0.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/h0.smali
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/b0.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/b0.smali
|
||||
@@ -669,8 +669,23 @@
|
||||
move-object v5, v1
|
||||
|
||||
|
||||
.line 289
|
||||
+ const v1, 0x52414443 # group key
|
||||
+
|
||||
+ invoke-interface {v7, v1}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open group around the cover
|
||||
+
|
||||
+ iget-object v1, v0, Lcom/tidal/android/feature/playerscreen/ui/h0;->b:Lcom/tidal/android/feature/playerscreen/ui/r$a; # player state
|
||||
+ iget-object v1, v0, Lcom/tidal/android/feature/playerscreen/ui/b0;->b:Lcom/tidal/android/feature/playerscreen/ui/s$a; # player state
|
||||
+
|
||||
+ iget-object v1, v1, Lcom/tidal/android/feature/playerscreen/ui/r$a;->j:Lcom/tidal/android/feature/playerscreen/ui/g; # current view mode
|
||||
+ iget-object v1, v1, Lcom/tidal/android/feature/playerscreen/ui/s$a;->j:Lcom/tidal/android/feature/playerscreen/ui/g; # current view mode
|
||||
+
|
||||
+ instance-of v1, v1, Lcom/tidal/android/feature/playerscreen/ui/g$a; # cover mode only
|
||||
+
|
||||
+ if-eqz v1, :radiant_after_cover # lyrics/credits mode -> skip cover
|
||||
+
|
||||
invoke-static/range {v2 .. v9}, Lcom/tidal/android/feature/playerscreen/ui/composables/CoverPagerKt;->c(Lcom/tidal/android/feature/playerscreen/ui/d;Lyl0/l;FLandroidx/compose/ui/Modifier;ZLandroidx/compose/runtime/Composer;II)V
|
||||
|
||||
invoke-static/range {v2 .. v9}, Lcom/tidal/android/feature/playerscreen/ui/composables/CoverPagerKt;->c(Lcom/tidal/android/feature/playerscreen/ui/d;Lam0/l;FLandroidx/compose/ui/Modifier;ZLandroidx/compose/runtime/Composer;II)V
|
||||
|
||||
+ :radiant_after_cover # skip target
|
||||
+ invoke-interface {v7}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close group
|
||||
+
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/composables/o1.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/composables/o1.smali
|
||||
@@ -64,26 +64,29 @@
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
.line 16
|
||||
.line 17
|
||||
- iget v2, p0, Lcom/tidal/android/feature/playerscreen/ui/composables/o1;->a:F
|
||||
+ const/high16 v2, 0x43200000 # 160f start dp
|
||||
|
||||
|
||||
.line 18
|
||||
.line 19
|
||||
invoke-interface {p1, v2}, Landroidx/compose/ui/unit/Density;->toPx-0680j_4(F)F
|
||||
|
||||
@@ -73,7 +73,13 @@
|
||||
.line 20
|
||||
.line 21
|
||||
.line 22
|
||||
@@ -22,16 +21,16 @@
|
||||
+ invoke-interface {p1, v3}, Landroidx/compose/ui/unit/Density;->toPx-0680j_4(F)F # dp to px
|
||||
+
|
||||
+ move-result v3 # endY in px
|
||||
|
||||
|
||||
.line 23
|
||||
const/16 v5, 0x8
|
||||
|
||||
.line 24
|
||||
@@ -82,9 +88,6 @@
|
||||
.line 25
|
||||
const/4 v6, 0x0
|
||||
|
||||
|
||||
- .line 26
|
||||
- const/4 v2, 0x0
|
||||
-
|
||||
.line 27
|
||||
const/4 v4, 0x0
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt$PlayerScreenPortrait$2$1.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt$PlayerScreenPortrait$2$1.smali
|
||||
@@ -231,7 +231,7 @@
|
||||
|
||||
.line 25
|
||||
.line 26
|
||||
- if-eqz p1, :cond_3
|
||||
+ goto :cond_3 # skip auto-hide branch
|
||||
|
||||
.line 27
|
||||
.line 28
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/PlayerViewModel.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerViewModel.smali
|
||||
@@ -1382,6 +1382,19 @@
|
||||
.line 109
|
||||
.line 110
|
||||
move-result v1
|
||||
+ sget v4, Lradiant/LyricsReinitGuard;->lastMediaId:I
|
||||
+ if-ne v1, v4, :rl_reinit_store
|
||||
+ sget-object v4, Lradiant/LyricsReinitGuard;->lastVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
+ if-ne v0, v4, :rl_reinit_store
|
||||
+ goto :rl_skip_reinit # only suppress duplicate initialization on the same ViewModel
|
||||
+ :rl_reinit_store
|
||||
+ sput v1, Lradiant/LyricsReinitGuard;->lastMediaId:I
|
||||
+ sput-object v0, Lradiant/LyricsReinitGuard;->lastVm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
+ goto :rl_reinit_ok
|
||||
+ :rl_skip_reinit
|
||||
+ sget-object v4, Lkotlin/u;->a:Lkotlin/u;
|
||||
+ return-object v4
|
||||
+ :rl_reinit_ok
|
||||
|
||||
.line 111
|
||||
invoke-static {v1}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
|
||||
@@ -1,18 +1,18 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/c0.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/c0.smali
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/m0.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/m0.smali
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
# virtual methods
|
||||
.method public final invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
- .locals 17
|
||||
+ .locals 26 # extra regs for AndroidView
|
||||
|
||||
|
||||
.line 1
|
||||
move-object/from16 v0, p0
|
||||
@@ -461,6 +461,64 @@
|
||||
@@ -460,6 +460,64 @@
|
||||
.line 200
|
||||
invoke-static/range {v2 .. v9}, Lcom/tidal/android/feature/playerscreen/ui/composables/LyricsKt;->a(Lcom/tidal/android/feature/playerscreen/ui/g;Lyl0/l;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/PaddingValues;Lyl0/l;Landroidx/compose/runtime/Composer;II)V
|
||||
|
||||
invoke-static/range {v2 .. v9}, Lcom/tidal/android/feature/playerscreen/ui/composables/LyricsKt;->a(Lcom/tidal/android/feature/playerscreen/ui/g;Lam0/l;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/PaddingValues;Lam0/l;Landroidx/compose/runtime/Composer;II)V
|
||||
|
||||
+ sget-object v17, Lradiant/SpvFactory;->a:Lradiant/SpvFactory; # progress view factory
|
||||
+
|
||||
+ sget-object v18, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion; # base modifier
|
||||
@@ -29,7 +29,7 @@
|
||||
+
|
||||
+ const/16 v24, 0x0 # synthetic null
|
||||
+
|
||||
+ invoke-static/range {v18 .. v24}, Landroidx/compose/foundation/ClickableKt;->clickable-XHw0xAI$default(Landroidx/compose/ui/Modifier;ZLjava/lang/String;Landroidx/compose/ui/semantics/Role;Lyl0/a;ILjava/lang/Object;)Landroidx/compose/ui/Modifier; # apply clickable
|
||||
+ invoke-static/range {v18 .. v24}, Landroidx/compose/foundation/ClickableKt;->clickable-XHw0xAI$default(Landroidx/compose/ui/Modifier;ZLjava/lang/String;Landroidx/compose/ui/semantics/Role;Lam0/a;ILjava/lang/Object;)Landroidx/compose/ui/Modifier; # apply clickable
|
||||
+
|
||||
+ move-result-object v23 # clickable modifier
|
||||
+
|
||||
@@ -69,7 +69,7 @@
|
||||
+
|
||||
+ const/16 v22, 0x4 # default mask
|
||||
+
|
||||
+ invoke-static/range {v17 .. v22}, Landroidx/compose/ui/viewinterop/AndroidView_androidKt;->AndroidView(Lyl0/l;Landroidx/compose/ui/Modifier;Lyl0/l;Landroidx/compose/runtime/Composer;II)V # mount progress pill
|
||||
+ invoke-static/range {v17 .. v22}, Landroidx/compose/ui/viewinterop/AndroidView_androidKt;->AndroidView(Lam0/l;Landroidx/compose/ui/Modifier;Lam0/l;Landroidx/compose/runtime/Composer;II)V # mount progress pill
|
||||
+
|
||||
.line 201
|
||||
.line 202
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
# rl-locals: com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali e( 79
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
|
||||
@@ -4945,7 +4945,11 @@
|
||||
const/4 v7, 0x0
|
||||
|
||||
.line 226
|
||||
- invoke-static {v7, v9, v4, v2, v10}, Lcom/tidal/android/feature/playerscreen/ui/composables/k1;->a(Landroidx/compose/ui/Modifier;Lyl0/a;ZLandroidx/compose/runtime/Composer;I)V
|
||||
+ const v10, 0x52414448 # empty group key
|
||||
@@ -5183,7 +5183,11 @@
|
||||
const/4 v12, 0x0
|
||||
|
||||
.line 259
|
||||
- invoke-static {v7, v8, v4, v15, v12}, Lcom/tidal/android/feature/playerscreen/ui/composables/k1;->a(Landroidx/compose/ui/Modifier;Lam0/a;ZLandroidx/compose/runtime/Composer;I)V
|
||||
+ const v7, 0x52414448 # empty group key
|
||||
+
|
||||
+ invoke-interface {v2, v10}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty
|
||||
+ invoke-interface {v15, v7}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty
|
||||
+
|
||||
+ invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close empty
|
||||
|
||||
.line 227
|
||||
invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V
|
||||
@@ -5720,6 +5724,22 @@
|
||||
:cond_51
|
||||
check-cast v4, Lyl0/a;
|
||||
|
||||
const/4 v2, 0x0
|
||||
|
||||
invoke-static {v13, v7, v2, v4}, Lcom/tidal/android/feature/playerscreen/ui/composables/l3;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lyl0/a;)V
|
||||
+ invoke-interface {v15}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close empty
|
||||
|
||||
.line 260
|
||||
invoke-interface {v15}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V
|
||||
@@ -5967,6 +5971,22 @@
|
||||
|
||||
invoke-static {v4, v3, v7, v6}, Lcom/tidal/android/feature/playerscreen/ui/composables/s3;->a(ILam0/a;Landroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;)V
|
||||
|
||||
+ new-instance v74, Landroidx/window/embedding/a2; # lyrics toggle lambda
|
||||
+
|
||||
+ new-instance v74, Lcom/tidal/android/feature/playerscreen/ui/g0; # lyrics toggle lambda
|
||||
+ move-object/from16 v75, p2 # action dispatcher
|
||||
+
|
||||
+ move-object/from16 v75, p5 # action dispatcher
|
||||
+ const/16 v76, 0x1 # lyrics action disc
|
||||
+
|
||||
+ const/16 v76, 0x0 # lyrics action disc
|
||||
+
|
||||
+ invoke-direct/range {v74 .. v76}, Lcom/tidal/android/feature/playerscreen/ui/g0;-><init>(Ljava/lang/Object;I)V # build lambda
|
||||
+ invoke-direct/range {v74 .. v76}, Landroidx/window/embedding/a2;-><init>(Ljava/lang/Object;I)V # build lambda
|
||||
+
|
||||
+ const/16 v71, 0x0 # changed flags
|
||||
+
|
||||
@@ -36,4 +32,8 @@
|
||||
+
|
||||
+ const/16 v73, 0x0 # null modifier
|
||||
+
|
||||
+ invoke-static/range {v71 .. v74}, Lradiant/SparkleButton;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lyl0/a;)V # render sparkle button
|
||||
+ invoke-static/range {v71 .. v74}, Lradiant/SparkleButton;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lam0/a;)V # render sparkle button
|
||||
+
|
||||
.line 365
|
||||
iget-object v3, v11, Lcom/tidal/android/feature/playerscreen/ui/s$a;->m:Lcom/tidal/android/feature/playerscreen/ui/p;
|
||||
|
||||
|
||||
@@ -1,50 +1,46 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
|
||||
@@ -5094,7 +5094,11 @@
|
||||
const/4 v13, 0x0
|
||||
|
||||
.line 247
|
||||
- invoke-static {v9, v0, v10, v2, v13}, Lcom/tidal/android/feature/playerscreen/ui/composables/BroadcastButtonKt;->b(Lcom/tidal/android/feature/playerscreen/ui/b;Lyl0/a;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V
|
||||
+ const v9, 0x52414244 # empty group key
|
||||
@@ -5328,7 +5328,11 @@
|
||||
const/4 v12, 0x0
|
||||
|
||||
.line 280
|
||||
- invoke-static {v8, v6, v12, v15, v3}, Lcom/tidal/android/feature/playerscreen/ui/composables/BroadcastButtonKt;->b(Lcom/tidal/android/feature/playerscreen/ui/b;Lam0/a;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V
|
||||
+ const v3, 0x52414244 # empty group key
|
||||
+
|
||||
+ invoke-interface {v2, v9}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty
|
||||
+ invoke-interface {v15, v3}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty
|
||||
+
|
||||
+ invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close empty
|
||||
|
||||
.line 248
|
||||
invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endNode()V
|
||||
@@ -5770,16 +5774,20 @@
|
||||
.line 335
|
||||
:cond_53
|
||||
- new-instance v4, Lcom/aspiro/wamp/tidalconnect/playback/i;
|
||||
-
|
||||
- invoke-direct {v4, v11, v1}, Lcom/aspiro/wamp/tidalconnect/playback/i;-><init>(Ljava/lang/Object;I)V
|
||||
-
|
||||
- .line 336
|
||||
- invoke-interface {v7, v4}, Landroidx/compose/runtime/Composer;->updateRememberedValue(Ljava/lang/Object;)V
|
||||
-
|
||||
- .line 337
|
||||
- :cond_54
|
||||
- check-cast v4, Lyl0/a;
|
||||
-
|
||||
- const/4 v2, 0x0
|
||||
-
|
||||
- invoke-static {v13, v7, v2, v4}, Lcom/tidal/android/feature/playerscreen/ui/composables/x4;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lyl0/a;)V
|
||||
+ new-instance v4, Lcom/aspiro/wamp/tidalconnect/playback/i; # connect click lambda factory
|
||||
+ invoke-interface {v15}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close empty
|
||||
|
||||
.line 281
|
||||
invoke-interface {v15}, Landroidx/compose/runtime/Composer;->endNode()V
|
||||
@@ -6156,11 +6160,11 @@
|
||||
.line 385
|
||||
:cond_61
|
||||
:goto_2e
|
||||
- new-instance v1, Lcom/tidal/android/feature/playerscreen/ui/e0;
|
||||
+ new-instance v1, Landroidx/window/embedding/b2; # connect click lambda (was share e0)
|
||||
|
||||
- const/4 v6, 0x0
|
||||
+ const/4 v6, 0x1 # connect-clicked disc
|
||||
|
||||
- invoke-direct {v1, v10, v6}, Lcom/tidal/android/feature/playerscreen/ui/e0;-><init>(Ljava/lang/Object;I)V
|
||||
+ invoke-direct {v1, v10, v6}, Landroidx/window/embedding/b2;-><init>(Ljava/lang/Object;I)V # build connect lambda
|
||||
|
||||
.line 386
|
||||
invoke-interface {v7, v1}, Landroidx/compose/runtime/Composer;->updateRememberedValue(Ljava/lang/Object;)V
|
||||
@@ -6169,9 +6173,13 @@
|
||||
:goto_2f
|
||||
check-cast v1, Lam0/a;
|
||||
|
||||
- const/4 v3, 0x0
|
||||
+ iget-object v8, v11, Lcom/tidal/android/feature/playerscreen/ui/s$a;->d:Lcom/tidal/android/feature/playerscreen/ui/b; # broadcast state
|
||||
+
|
||||
+ move-object/from16 v6, p5 # action dispatcher
|
||||
+ const/4 v3, 0x0 # null modifier
|
||||
+
|
||||
+ const/4 v8, 0x1 # connect-clicked disc
|
||||
+
|
||||
+ invoke-direct {v4, v6, v8}, Lcom/aspiro/wamp/tidalconnect/playback/i;-><init>(Ljava/lang/Object;I)V # build lambda
|
||||
+
|
||||
+ invoke-interface {v7, v4}, Landroidx/compose/runtime/Composer;->updateRememberedValue(Ljava/lang/Object;)V # cache lambda
|
||||
+
|
||||
+ :cond_54 # cache join
|
||||
+ check-cast v4, Lyl0/a; # cast to Function0
|
||||
+
|
||||
+ iget-object v8, v10, Lcom/tidal/android/feature/playerscreen/ui/r$a;->d:Lcom/tidal/android/feature/playerscreen/ui/b; # broadcast state
|
||||
+
|
||||
+ const/4 v2, 0x0 # changed flags
|
||||
+
|
||||
+ invoke-static {v8, v4, v13, v7, v2}, Lcom/tidal/android/feature/playerscreen/ui/composables/BroadcastButtonKt;->b(Lcom/tidal/android/feature/playerscreen/ui/b;Lyl0/a;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V # render connect button
|
||||
+ const/4 v6, 0x0 # changed flags
|
||||
|
||||
- invoke-static {v6, v1, v7, v3}, Lcom/tidal/android/feature/playerscreen/ui/composables/e5;->a(ILam0/a;Landroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;)V
|
||||
+ invoke-static {v8, v1, v3, v7, v6}, Lcom/tidal/android/feature/playerscreen/ui/composables/BroadcastButtonKt;->b(Lcom/tidal/android/feature/playerscreen/ui/b;Lam0/a;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V # render connect button
|
||||
|
||||
if-ne v0, v2, :cond_62
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
--- a/cg0/h.smali
|
||||
+++ b/cg0/h.smali
|
||||
@@ -363,6 +363,23 @@
|
||||
.method public static final d(Lcom/tidal/sdk/tidalapi/generated/models/TracksResourceObject;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/Map;)Lcom/tidal/android/tidalapi/domain/model/o;
|
||||
.locals 23
|
||||
|
||||
+ invoke-virtual/range {p0 .. p0}, Lcom/tidal/sdk/tidalapi/generated/models/TracksResourceObject;->getAttributes()Lcom/tidal/sdk/tidalapi/generated/models/TracksAttributes;
|
||||
+
|
||||
+ move-result-object v1
|
||||
+
|
||||
+ if-eqz v1, :rl_isrc_skip
|
||||
+
|
||||
+ invoke-virtual/range {p0 .. p0}, Lcom/tidal/sdk/tidalapi/generated/models/TracksResourceObject;->getId()Ljava/lang/String;
|
||||
+
|
||||
+ move-result-object v0
|
||||
+
|
||||
+ invoke-virtual {v1}, Lcom/tidal/sdk/tidalapi/generated/models/TracksAttributes;->getIsrc()Ljava/lang/String;
|
||||
+
|
||||
+ move-result-object v1
|
||||
+
|
||||
+ invoke-static {v0, v1}, Lradiant/IsrcCache;->put(Ljava/lang/String;Ljava/lang/String;)V
|
||||
+
|
||||
+ :rl_isrc_skip
|
||||
move-object/from16 v0, p4
|
||||
|
||||
move-object/from16 v1, p7
|
||||
@@ -0,0 +1,25 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a.smali
|
||||
@@ -58,6 +58,8 @@
|
||||
.line 5
|
||||
int-to-long p1, p1
|
||||
|
||||
+ invoke-static {p1, p2}, Lradiant/WordLyrics;->onProgress(J)V
|
||||
+
|
||||
.line 6
|
||||
iget-object v0, p0, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a;->a:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||
|
||||
@@ -191,6 +193,13 @@
|
||||
|
||||
.line 67
|
||||
:cond_3
|
||||
+ sget-boolean v6, Lradiant/RLAPILyricsHook;->isRlState:Z
|
||||
+
|
||||
+ if-eqz v6, :radiant_skip
|
||||
+
|
||||
+ const/4 v5, -0x1
|
||||
+
|
||||
+ :radiant_skip
|
||||
if-gez v4, :cond_4
|
||||
|
||||
.line 68
|
||||
@@ -0,0 +1,80 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/composables/f2.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/composables/f2.smali
|
||||
@@ -932,7 +932,7 @@
|
||||
|
||||
# virtual methods
|
||||
.method public final invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
- .locals 28
|
||||
+ .locals 40
|
||||
|
||||
.line 1
|
||||
move-object/from16 v0, p0
|
||||
@@ -1158,6 +1158,8 @@
|
||||
.line 100
|
||||
check-cast v1, Lcom/tidal/android/feature/playerscreen/ui/f;
|
||||
|
||||
+ move/16 v33, v2 # lazy item index
|
||||
+
|
||||
.line 101
|
||||
.line 102
|
||||
const v3, 0x6b82e849
|
||||
@@ -1646,6 +1648,26 @@
|
||||
.line 331
|
||||
iget-object v3, v1, Lcom/tidal/android/feature/playerscreen/ui/f;->a:Ljava/lang/String;
|
||||
|
||||
+ iget v4, v0, Lcom/tidal/android/feature/playerscreen/ui/composables/f2;->b:I
|
||||
+
|
||||
+ move/16 v34, v4 # TIDALs active line
|
||||
+
|
||||
+ iget-object v4, v1, Lcom/tidal/android/feature/playerscreen/ui/f;->a:Ljava/lang/String;
|
||||
+
|
||||
+ move-object/16 v35, v4 # plain line text
|
||||
+
|
||||
+ iget-wide v4, v0, Lcom/tidal/android/feature/playerscreen/ui/composables/f2;->d:J
|
||||
+
|
||||
+ move-wide/16 v36, v4 # sung colour
|
||||
+
|
||||
+ iget-wide v4, v0, Lcom/tidal/android/feature/playerscreen/ui/composables/f2;->e:J
|
||||
+
|
||||
+ move-wide/16 v38, v4 # unsung colour
|
||||
+
|
||||
+ invoke-static/range {v33 .. v39}, Lradiant/WordLyrics;->renderSyl(IILjava/lang/String;JJ)Landroidx/compose/ui/text/AnnotatedString;
|
||||
+
|
||||
+ move-result-object v3 # syllable-wiped line
|
||||
+
|
||||
.line 332
|
||||
.line 333
|
||||
invoke-interface {v2}, Landroidx/compose/runtime/State;->getValue()Ljava/lang/Object;
|
||||
@@ -1672,7 +1694,7 @@
|
||||
|
||||
.line 344
|
||||
.line 345
|
||||
- const v27, 0xfff8
|
||||
+ const v27, 0x7ff8 # $default: pass onTextLayout
|
||||
|
||||
.line 346
|
||||
.line 347
|
||||
@@ -1727,7 +1749,13 @@
|
||||
|
||||
.line 369
|
||||
.line 370
|
||||
- const/16 v22, 0x0
|
||||
+ move/from16 v1, v33 # line index
|
||||
+
|
||||
+ invoke-static {v1}, Lradiant/SylLayout;->cb(I)Lam0/l; # per-line layout capture
|
||||
+
|
||||
+ move-result-object v1
|
||||
+
|
||||
+ move-object/from16 v22, v1 # onTextLayout
|
||||
|
||||
.line 371
|
||||
.line 372
|
||||
@@ -1743,7 +1771,7 @@
|
||||
|
||||
.line 377
|
||||
.line 378
|
||||
- invoke-static/range {v3 .. v27}, Landroidx/compose/material3/TextKt;->Text--4IGK_g(Ljava/lang/String;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZIILam0/l;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V
|
||||
+ invoke-static/range {v3 .. v27}, Landroidx/compose/material3/TextKt;->Text--4IGK_g(Landroidx/compose/ui/text/AnnotatedString;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZILjava/util/Map;Lam0/l;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V
|
||||
|
||||
.line 379
|
||||
.line 380
|
||||
@@ -0,0 +1,56 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/composables/f2.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/composables/f2.smali
|
||||
@@ -87,7 +87,7 @@
|
||||
|
||||
# virtual methods
|
||||
.method public final invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
- .locals 28
|
||||
+ .locals 40
|
||||
|
||||
.line 1
|
||||
move-object/from16 v0, p0
|
||||
@@ -313,6 +313,8 @@
|
||||
.line 100
|
||||
check-cast v1, Lcom/tidal/android/feature/playerscreen/ui/f;
|
||||
|
||||
+ move/16 v33, v2
|
||||
+
|
||||
.line 101
|
||||
.line 102
|
||||
const v3, 0x6b82e849
|
||||
@@ -801,6 +803,26 @@
|
||||
.line 331
|
||||
iget-object v3, v1, Lcom/tidal/android/feature/playerscreen/ui/f;->a:Ljava/lang/String;
|
||||
|
||||
+ iget v4, v0, Lcom/tidal/android/feature/playerscreen/ui/composables/f2;->b:I
|
||||
+
|
||||
+ move/16 v34, v4
|
||||
+
|
||||
+ iget-object v4, v1, Lcom/tidal/android/feature/playerscreen/ui/f;->a:Ljava/lang/String;
|
||||
+
|
||||
+ move-object/16 v35, v4
|
||||
+
|
||||
+ iget-wide v4, v0, Lcom/tidal/android/feature/playerscreen/ui/composables/f2;->d:J
|
||||
+
|
||||
+ move-wide/16 v36, v4
|
||||
+
|
||||
+ iget-wide v4, v0, Lcom/tidal/android/feature/playerscreen/ui/composables/f2;->e:J
|
||||
+
|
||||
+ move-wide/16 v38, v4
|
||||
+
|
||||
+ invoke-static/range {v33 .. v39}, Lradiant/WordLyrics;->render(IILjava/lang/String;JJ)Landroidx/compose/ui/text/AnnotatedString;
|
||||
+
|
||||
+ move-result-object v3
|
||||
+
|
||||
.line 332
|
||||
.line 333
|
||||
invoke-interface {v2}, Landroidx/compose/runtime/State;->getValue()Ljava/lang/Object;
|
||||
@@ -898,7 +920,7 @@
|
||||
|
||||
.line 377
|
||||
.line 378
|
||||
- invoke-static/range {v3 .. v27}, Landroidx/compose/material3/TextKt;->Text--4IGK_g(Ljava/lang/String;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZIILam0/l;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V
|
||||
+ invoke-static/range {v3 .. v27}, Landroidx/compose/material3/TextKt;->Text--4IGK_g(Landroidx/compose/ui/text/AnnotatedString;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZILjava/util/Map;Lam0/l;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V
|
||||
|
||||
.line 379
|
||||
.line 380
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user