14 Commits

Author SHA1 Message Date
meoware.exe ee8b25330d Merge pull request #28 from meowarex/dev
New Patch: Cover Everywhere - WIP
2026-05-25 21:13:34 +10:00
meoware.exe 193d5ad90d Overhaul Manager Updates & Logging 2026-05-25 21:13:00 +10:00
meoware.exe 942742e86e New Patch: Cover Everywhere - WIP 2026-05-25 20:27:03 +10:00
meoware.exe 0b039110c9 Merge pull request #26 from meowarex/dev
Fix Missing Dependency
2026-05-25 04:35:38 +10:00
meoware.exe 045bbbab9a Fix Missing Dependency 2026-05-25 04:35:06 +10:00
meoware.exe 932f2a85dc Merge pull request #25 from meowarex/dev
Update data.json
2026-05-25 04:27:15 +10:00
meoware.exe fc002615f4 Update data.json 2026-05-25 04:26:49 +10:00
meoware.exe c89dd54fa6 Merge pull request #24 from meowarex/dev
New Patches: RL API & Keep Controls Visible
2026-05-25 04:19:08 +10:00
meoware.exe 53a4341d6b New Patches: RL API & Keep Controls Visible 2026-05-25 04:17:44 +10:00
meoware.exe c81d2edba3 Merge pull request #23 from meowarex/dev
Fix Update Status & Dynamic Labels
2026-05-25 00:51:52 +10:00
meoware.exe 9180129afc Fix Update Status & Dynamic Labels 2026-05-25 00:51:05 +10:00
meoware.exe d9de3207f5 Merge pull request #22 from meowarex/dev
Update to TIDAL 2.192.1-9090
2026-05-25 00:14:30 +10:00
meoware.exe 59fe232ae4 Update to TIDAL 2.192.1-9090 2026-05-25 00:13:46 +10:00
meoware.exe e105a3eb35 Fix Lyrics Fade Region 2026-05-24 22:55:00 +10:00
55 changed files with 2642 additions and 957 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ jobs:
if [[ "$tidal_src" == *.apkm ]]; then if [[ "$tidal_src" == *.apkm ]]; then
echo "Merging splits from $tidal_src via APKEditor" echo "Merging splits from $tidal_src via APKEditor"
curl --fail --location --retry 3 --retry-delay 2 -sSo /tmp/APKEditor.jar \ curl --fail --location --retry 3 --retry-delay 2 -sSo /tmp/APKEditor.jar \
https://github.com/REAndroid/APKEditor/releases/download/V1.4.3/APKEditor-1.4.3.jar https://github.com/REAndroid/APKEditor/releases/download/V1.4.9/APKEditor-1.4.9.jar
java -jar /tmp/APKEditor.jar m -i "$tidal_src" -o ./dist/tidal-stock.apk java -jar /tmp/APKEditor.jar m -i "$tidal_src" -o ./dist/tidal-stock.apk
echo "Merged tidal-stock.apk:" echo "Merged tidal-stock.apk:"
ls -la ./dist/tidal-stock.apk ls -la ./dist/tidal-stock.apk
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -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.componentopts.ComponentOptionsModel
import com.meowarex.rlmobile.ui.screens.home.HomeModel import com.meowarex.rlmobile.ui.screens.home.HomeModel
import com.meowarex.rlmobile.ui.screens.log.LogScreenModel 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.patching.PatchingScreenModel
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionsModel import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionsModel
import com.meowarex.rlmobile.ui.screens.permissions.PermissionsModel import com.meowarex.rlmobile.ui.screens.permissions.PermissionsModel
@@ -64,7 +63,6 @@ class ManagerApplication : Application() {
factoryOf(::PatchOptionsModel) factoryOf(::PatchOptionsModel)
factoryOf(::ComponentOptionsModel) factoryOf(::ComponentOptionsModel)
factoryOf(::LogScreenModel) factoryOf(::LogScreenModel)
factoryOf(::LogsListScreenModel)
factoryOf(::PermissionsModel) factoryOf(::PermissionsModel)
viewModelOf(::UpdaterViewModel) viewModelOf(::UpdaterViewModel)
}) })
@@ -12,8 +12,6 @@ import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
import com.meowarex.rlmobile.util.* import com.meowarex.rlmobile.util.*
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import java.io.IOException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@@ -21,52 +19,19 @@ import kotlin.time.Duration
import kotlin.time.Instant import kotlin.time.Instant
/** /**
* Central manager for storing all attempted installations and * In-memory store for the most recent install attempts
* their associated logs/crashes (not including manager crashes themselves).
*/ */
class InstallLogManager( class InstallLogManager(
private val application: Application, private val application: Application,
private val prefs: PreferencesManager, 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>()
/** fun fetchInstallData(id: String): InstallLogData? = synchronized(entries) {
* Lists all the install data entries that exist on disk, sorted decreasing by entries[id]
* 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 }
} }
/**
* 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( suspend fun storeInstallData(
id: String, id: String,
installDate: Instant, installDate: Instant,
@@ -75,8 +40,6 @@ class InstallLogManager(
log: String, log: String,
error: Throwable?, error: Throwable?,
) { ) {
val path = logsDir.resolve("$id.json")
val data = InstallLogData( val data = InstallLogData(
id = id, id = id,
installDate = installDate, installDate = installDate,
@@ -87,16 +50,16 @@ class InstallLogManager(
errorStacktrace = error?.let { Log.getStackTraceString(it).trimEnd() }, errorStacktrace = error?.let { Log.getStackTraceString(it).trimEnd() },
) )
try { synchronized(entries) {
path.writeText(json.encodeToString(data)) entries[id] = data
} catch (e: IOException) { // Keep only the most recent few in memory.
Log.e(BuildConfig.TAG, "Failed to write log to disk", e) 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") @Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants")
@SuppressLint("UsableSpace") @SuppressLint("UsableSpace")
suspend fun getEnvironmentInfo(): String { suspend fun getEnvironmentInfo(): String {
@@ -139,6 +102,10 @@ class InstallLogManager(
SOC: $soc SOC: $soc
""".trimIndent() """.trimIndent()
} }
private companion object {
const val MAX_ENTRIES = 8
}
} }
@Immutable @Immutable
@@ -12,7 +12,6 @@ class PreferencesManager(preferences: SharedPreferences) : BasePreferenceManager
var devMode by booleanPreference("dev_mode", false) var devMode by booleanPreference("dev_mode", false)
var installer by enumPreference<InstallerSetting>("installer", InstallerSetting.PackageInstaller) var installer by enumPreference<InstallerSetting>("installer", InstallerSetting.PackageInstaller)
var keepPatchedApks by booleanPreference("keep_patched_apks", false) var keepPatchedApks by booleanPreference("keep_patched_apks", false)
var showNetworkWarning by booleanPreference("show_network_warning", true)
var showPlayProtectWarning by booleanPreference("show_play_protect_warning", true) var showPlayProtectWarning by booleanPreference("show_play_protect_warning", true)
var autoUpdateCheck by booleanPreference("auto_update_check", true) var autoUpdateCheck by booleanPreference("auto_update_check", true)
@@ -92,18 +92,25 @@ class SmaliPatchStep(
container.log("Recorded rl-locals bump: $smaliPath method≈\"$methodSubstring\" >= $newValue") container.log("Recorded rl-locals bump: $smaliPath method≈\"$methodSubstring\" >= $newValue")
} }
val targetLine = lines.firstOrNull { it.startsWith("--- a/") } // Split into per-target sections — a single .patch may contain multiple
?: throw Error("Patch $patchFile is missing a '--- a/...' header") // `--- a/...` blocks targeting different classes.
val fullClassName = targetLine val sections = splitMultiTargetPatch(lines)
.removePrefix("--- a/") if (sections.isEmpty()) {
.removeSuffix(".smali") throw Error("Patch $patchFile is missing a '--- a/...' header")
.trim() }
val patch = LoadedPatch( for (section in sections) {
fullClassName = fullClassName, val targetLine = section.first { it.startsWith("--- a/") }
patch = UnifiedDiffUtils.parseUnifiedDiff(lines), val fullClassName = targetLine
) .removePrefix("--- a/")
patches.add(patch) .removeSuffix(".smali")
container.log("Loaded patch file $patchFile for class ${patch.fullClassName}") .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) { } catch (t: Throwable) {
throw Error("Failed to parse patch file $patchFile", t) throw Error("Failed to parse patch file $patchFile", t)
} }
@@ -314,6 +321,28 @@ class SmaliPatchStep(
private companion object { private companion object {
val LOCALS_DIRECTIVE = Regex("""^#\s*rl-locals:\s+(\S+)\s+(\S+)\s+(\d+)\s*$""") val LOCALS_DIRECTIVE = Regex("""^#\s*rl-locals:\s+(\S+)\s+(\S+)\s+(\d+)\s*$""")
} }
/**
* 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)
}
}
} }
private data class LoadedPatch( private data class LoadedPatch(
@@ -1,96 +0,0 @@
package com.meowarex.rlmobile.ui.components.dialogs
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import com.meowarex.rlmobile.R
@Composable
fun NetworkWarningDialog(
onConfirm: (neverShow: Boolean) -> Unit,
onDismiss: (neverShow: Boolean) -> Unit,
) {
val interactionSource = remember(::MutableInteractionSource)
var neverShow by rememberSaveable { mutableStateOf(false) }
val rememberedNeverShow by rememberUpdatedState(neverShow)
AlertDialog(
onDismissRequest = { onDismiss(rememberedNeverShow) },
properties = DialogProperties(
dismissOnClickOutside = false,
),
confirmButton = {
FilledTonalButton(
onClick = { onConfirm(rememberedNeverShow) },
colors = ButtonDefaults.filledTonalButtonColors(
containerColor = MaterialTheme.colorScheme.error,
contentColor = MaterialTheme.colorScheme.onError,
),
) {
Text(stringResource(R.string.action_continue))
}
},
dismissButton = {
TextButton(
onClick = { onDismiss(rememberedNeverShow) },
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.onErrorContainer
),
) {
Text(stringResource(R.string.navigation_back))
}
},
title = { Text(stringResource(R.string.network_warning_title)) },
text = {
Column(
verticalArrangement = Arrangement.spacedBy(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(R.string.network_warning_body),
textAlign = TextAlign.Center,
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = { neverShow = !rememberedNeverShow },
)
.padding(end = 16.dp)
) {
Checkbox(
checked = neverShow,
onCheckedChange = { neverShow = it },
interactionSource = interactionSource,
)
Text(stringResource(R.string.network_warning_disable))
}
}
},
icon = {
Icon(
painter = painterResource(R.drawable.ic_warning),
contentDescription = null,
modifier = Modifier.size(32.dp),
)
},
containerColor = MaterialTheme.colorScheme.errorContainer,
iconContentColor = MaterialTheme.colorScheme.onErrorContainer,
titleContentColor = MaterialTheme.colorScheme.onErrorContainer,
textContentColor = MaterialTheme.colorScheme.onErrorContainer,
)
}
@@ -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 = {},
)
}
}
@@ -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.components.dialogs.NetworkWarningDialog
import com.meowarex.rlmobile.ui.theme.ManagerTheme
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun NetworkWarningDialogPreview() {
ManagerTheme {
NetworkWarningDialog(
onConfirm = {},
onDismiss = {},
)
}
}
@@ -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:18AM",
durationSecs = 18.555f,
stacktracePreview = null,
),
LogEntry(
id = UUID.randomUUID().toString(),
isError = true,
installDate = "7 min. ago, 10:17AM",
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:37PM",
durationSecs = 58.439f,
stacktracePreview = null,
),
LogEntry(
id = UUID.randomUUID().toString(),
isError = true,
installDate = "Yesterday, 11:17PM",
durationSecs = 24.405f,
stacktracePreview = persistentListOf(
"java.lang.Error: Installation was aborted or cancelled",
),
),
LogEntry(
id = UUID.randomUUID().toString(),
isError = true,
installDate = "Yesterday, 11:17PM",
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:11PM",
durationSecs = 210.539f,
stacktracePreview = null,
),
)
@@ -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 = {},
)
}
}
@@ -117,18 +117,22 @@ class HomeModel(
val remote = remoteDataJson val remote = remoteDataJson
val currentPatches = remote?.patchesVersion?.toString() val currentPatches = remote?.patchesVersion?.toString()
val installedPatches = installMetadata?.patchesVersion?.toString()
val previousPatches = initialPrefPatchesVersion.ifEmpty { val previousPatches = initialPrefPatchesVersion.ifEmpty {
installMetadata?.patchesVersion?.toString().orEmpty() installedPatches.orEmpty()
} }
val patchesFrom = previousPatches.takeIf { it.isNotEmpty() } val patchesFrom = previousPatches.takeIf { it.isNotEmpty() }
val patchesTo = currentPatches ?: previousPatches.ifEmpty { "?" } val patchesTo = currentPatches ?: previousPatches.ifEmpty { "?" }
val patchesUpdateAvailable = installedPatches != null
&& currentPatches != null
&& installedPatches != currentPatches
add( add(
VersionDelta( VersionDelta(
label = application.getString(R.string.manager_update_row_patches), label = application.getString(R.string.manager_update_row_patches),
iconRes = R.drawable.ic_extension, iconRes = R.drawable.ic_extension,
from = patchesFrom, from = patchesFrom,
to = patchesTo, to = patchesTo,
tag = if (patchesFrom != null && patchesFrom != patchesTo) tag = if (patchesUpdateAvailable)
application.getString(R.string.manager_update_tag_available) else null, application.getString(R.string.manager_update_tag_available) else null,
) )
) )
@@ -142,13 +146,16 @@ class HomeModel(
val tidalTo = currentTidal?.toString() val tidalTo = currentTidal?.toString()
?: previousTidal.takeIf { it > 0 }?.toString() ?: previousTidal.takeIf { it > 0 }?.toString()
?: "?" ?: "?"
val tidalUpdateAvailable = installedTidalVersionCode > 0
&& currentTidal != null
&& installedTidalVersionCode != currentTidal
add( add(
VersionDelta( VersionDelta(
label = application.getString(R.string.manager_update_row_tidal), label = application.getString(R.string.manager_update_row_tidal),
iconRes = R.drawable.ic_music_note, iconRes = R.drawable.ic_music_note,
from = tidalFrom, from = tidalFrom,
to = tidalTo, to = tidalTo,
tag = if (tidalFrom != null && tidalFrom != tidalTo) tag = if (tidalUpdateAvailable)
application.getString(R.string.manager_update_tag_available) else null, application.getString(R.string.manager_update_tag_available) else null,
) )
) )
@@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.activity.ComponentActivity
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource 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.components.Tag
import com.meowarex.rlmobile.ui.screens.about.AboutScreen import com.meowarex.rlmobile.ui.screens.about.AboutScreen
import com.meowarex.rlmobile.ui.screens.home.components.CommitList 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.patchopts.PatchOptionsScreen
import com.meowarex.rlmobile.ui.screens.settings.SettingsScreen import com.meowarex.rlmobile.ui.screens.settings.SettingsScreen
import com.meowarex.rlmobile.ui.widgets.managerupdate.ManagerUpdateDialog import com.meowarex.rlmobile.ui.widgets.managerupdate.ManagerUpdateDialog
import com.meowarex.rlmobile.ui.widgets.updater.UpdaterViewModel
import com.meowarex.rlmobile.util.* import com.meowarex.rlmobile.util.*
import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.koin.androidx.compose.koinViewModel
@Parcelize @Parcelize
class HomeScreen : Screen, Parcelable { class HomeScreen : Screen, Parcelable {
@@ -50,6 +52,9 @@ class HomeScreen : Screen, Parcelable {
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val model = koinScreenModel<HomeModel>() val model = koinScreenModel<HomeModel>()
val activity = LocalContext.current as ComponentActivity
val updater = koinViewModel<UpdaterViewModel>(viewModelStoreOwner = activity)
val managerUpdateAvailable = updater.targetVersion != null
LifecycleResumeEffect(Unit) { LifecycleResumeEffect(Unit) {
model.refresh(delay = true) model.refresh(delay = true)
@@ -67,18 +72,20 @@ class HomeScreen : Screen, Parcelable {
contentDescription = stringResource(R.string.navigation_refresh), 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()) }) { IconButton(onClick = { navigator.push(AboutScreen()) }) {
Icon( Icon(
painterResource(R.drawable.ic_info), painterResource(R.drawable.ic_info),
contentDescription = stringResource(R.string.navigation_about), 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()) }) { IconButton(onClick = { navigator.push(SettingsScreen()) }) {
Icon( Icon(
painterResource(R.drawable.ic_settings), painterResource(R.drawable.ic_settings),
@@ -107,6 +114,7 @@ class HomeScreen : Screen, Parcelable {
is HomeState.Loaded -> HomeContent( is HomeState.Loaded -> HomeContent(
state = state, state = state,
commits = model.commits, commits = model.commits,
managerUpdateAvailable = managerUpdateAvailable,
onInstall = { navigator.pushOnce(PatchOptionsScreen()) }, onInstall = { navigator.pushOnce(PatchOptionsScreen()) },
onRepatch = { onRepatch = {
scope.launchIO { scope.launchIO {
@@ -134,6 +142,7 @@ class HomeScreen : Screen, Parcelable {
private fun ColumnScope.HomeContent( private fun ColumnScope.HomeContent(
state: HomeState.Loaded, state: HomeState.Loaded,
commits: kotlinx.coroutines.flow.Flow<androidx.paging.PagingData<com.meowarex.rlmobile.network.models.GithubCommit>>, commits: kotlinx.coroutines.flow.Flow<androidx.paging.PagingData<com.meowarex.rlmobile.network.models.GithubCommit>>,
managerUpdateAvailable: Boolean,
onInstall: () -> Unit, onInstall: () -> Unit,
onRepatch: () -> Unit, onRepatch: () -> Unit,
onLaunch: () -> Unit, onLaunch: () -> Unit,
@@ -200,15 +209,19 @@ private fun ColumnScope.HomeContent(
} }
} }
val blockedByManagerUpdate = managerUpdateAvailable && (patchesBehind || tidalBehind)
Button( Button(
onClick = if (install == null) onInstall else onRepatch, onClick = if (install == null) onInstall else onRepatch,
enabled = state.latestTidalVersionCode != null, enabled = state.latestTidalVersionCode != null && !blockedByManagerUpdate,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { ) {
val label = when { val label = when {
blockedByManagerUpdate -> "Manager Update Required"
state.latestTidalVersionCode == null -> "Loading…" state.latestTidalVersionCode == null -> "Loading…"
install == null -> "Install" install == null -> "Install"
install.isUpToDate == false -> "Update" patchesBehind && tidalBehind -> "Update Patches & TIDAL"
patchesBehind -> "Update Patches"
tidalBehind -> "Update TIDAL"
else -> "Repatch" else -> "Repatch"
} }
Text( Text(
@@ -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(),
)
}
}
}
}
@@ -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>?,
)
@@ -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,
)
}
}
}
}
}
@@ -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)
)
}
}
)
}
@@ -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),
)
}
}
}
@@ -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))
}
}
)
}
@@ -28,7 +28,6 @@ import com.meowarex.rlmobile.patcher.steps.StepGroup
import com.meowarex.rlmobile.ui.components.MainActionButton import com.meowarex.rlmobile.ui.components.MainActionButton
import com.meowarex.rlmobile.ui.components.Wakelock import com.meowarex.rlmobile.ui.components.Wakelock
import com.meowarex.rlmobile.ui.components.dialogs.InstallerAbortDialog import com.meowarex.rlmobile.ui.components.dialogs.InstallerAbortDialog
import com.meowarex.rlmobile.ui.components.dialogs.NetworkWarningDialog
import com.meowarex.rlmobile.ui.screens.log.LogScreen import com.meowarex.rlmobile.ui.screens.log.LogScreen
import com.meowarex.rlmobile.ui.screens.patching.components.* import com.meowarex.rlmobile.ui.screens.patching.components.*
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
@@ -111,19 +110,6 @@ class PatchingScreen(
listState.animateScrollToItem(0) listState.animateScrollToItem(0)
} }
if (model.showNetworkWarningDialog) {
NetworkWarningDialog(
onConfirm = { neverShow ->
model.hideNetworkWarning(neverShow)
model.install()
},
onDismiss = { neverShow ->
model.hideNetworkWarning(neverShow)
navigator.pop()
},
)
}
if (showAbortWarning) { if (showAbortWarning) {
InstallerAbortDialog( InstallerAbortDialog(
onDismiss = { showAbortWarning = false }, onDismiss = { showAbortWarning = false },
@@ -41,9 +41,6 @@ class PatchingScreenModel(
val devMode get() = prefs.devMode val devMode get() = prefs.devMode
var showNetworkWarningDialog by mutableStateOf(!alreadyShownNetworkWarning && application.isNetworkDangerous())
private set
var steps by mutableStateOf<ImmutableMap<StepGroup, ImmutableList<Step>>?>(null) var steps by mutableStateOf<ImmutableMap<StepGroup, ImmutableList<Step>>?>(null)
private set private set
@@ -52,11 +49,7 @@ class PatchingScreenModel(
private set private set
init { init {
if (!prefs.showNetworkWarning) install()
showNetworkWarningDialog = false
if (!showNetworkWarningDialog)
install()
// Rotate fun facts every so often // Rotate fun facts every so often
screenModelScope.launch { screenModelScope.launch {
@@ -67,12 +60,6 @@ class PatchingScreenModel(
} }
} }
fun hideNetworkWarning(neverShow: Boolean) {
showNetworkWarningDialog = false
alreadyShownNetworkWarning = true
prefs.showNetworkWarning = !neverShow
}
fun launchApp() { fun launchApp() {
if (state.value !is PatchingScreenState.Success) if (state.value !is PatchingScreenState.Success)
return return
@@ -194,9 +181,6 @@ class PatchingScreenModel(
} }
companion object { companion object {
// Global state to avoid showing the warning more than once per launch
private var alreadyShownNetworkWarning = false
/** /**
* Random fun facts to show on the installation screen. * Random fun facts to show on the installation screen.
*/ */
@@ -4,19 +4,31 @@ import androidx.annotation.StringRes
import com.meowarex.rlmobile.R import com.meowarex.rlmobile.R
enum class KnownPatch( enum class KnownPatch(
/**
* Numeric display order in the patch options list. Lower = higher up.
*
* Convention: main patches use multiples of 10 (10, 20, 30, …). Patches
* that act as helpers/dependencies of a main patch get offsets adjacent to
* the requirer (e.g. main at 40, helpers at 41, 42, 43). DebugMenuUnlock
* is pinned to 100 to keep it at the bottom of the list.
*/
val order: Int,
val fileNames: List<String>, val fileNames: List<String>,
@StringRes val titleRes: Int, @StringRes val titleRes: Int,
@StringRes val descRes: Int, @StringRes val descRes: Int,
val requires: List<KnownPatch> = emptyList(), val requires: List<KnownPatch> = emptyList(),
val disables: List<KnownPatch> = emptyList(), val disables: List<KnownPatch> = emptyList(),
) { ) {
// Dependency-first order (later refs need backward resolution) // Dependency-first order (later refs need backward resolution).
// The `order` field controls display order; declaration order doesn't matter.
LyricsDisableCover( LyricsDisableCover(
order = 41,
fileNames = listOf("lyrics-disable-cover.patch"), fileNames = listOf("lyrics-disable-cover.patch"),
titleRes = R.string.patch_lyrics_disable_cover_title, titleRes = R.string.patch_lyrics_disable_cover_title,
descRes = R.string.patch_lyrics_disable_cover_desc, descRes = R.string.patch_lyrics_disable_cover_desc,
), ),
LyricsReplaceLyricsButton( LyricsReplaceLyricsButton(
order = 42,
fileNames = listOf( fileNames = listOf(
"lyrics-replace-lyrics-button.patch", "lyrics-replace-lyrics-button.patch",
"lyrics-sparkle-conditional-visibility.patch", "lyrics-sparkle-conditional-visibility.patch",
@@ -25,30 +37,60 @@ enum class KnownPatch(
descRes = R.string.patch_lyrics_replace_button_desc, descRes = R.string.patch_lyrics_replace_button_desc,
), ),
LyricsReplaceShareButton( LyricsReplaceShareButton(
order = 43,
fileNames = listOf("lyrics-replace-share-button.patch"), fileNames = listOf("lyrics-replace-share-button.patch"),
titleRes = R.string.patch_lyrics_replace_share_button_title, titleRes = R.string.patch_lyrics_replace_share_button_title,
descRes = R.string.patch_lyrics_replace_share_button_desc, descRes = R.string.patch_lyrics_replace_share_button_desc,
), ),
LyricsRlApi(
order = 20,
fileNames = listOf(
"lyrics-rl-api.patch",
"lyrics-rl-api-observer.patch",
),
titleRes = R.string.patch_lyrics_rl_api_title,
descRes = R.string.patch_lyrics_rl_api_desc,
),
LyricsKeepControlsVisible(
order = 60,
fileNames = listOf("lyrics-keep-controls-visible.patch"),
titleRes = R.string.patch_lyrics_keep_controls_title,
descRes = R.string.patch_lyrics_keep_controls_desc,
),
PlayerBackdrop( PlayerBackdrop(
order = 30,
fileNames = listOf("player-backdrop.patch"), fileNames = listOf("player-backdrop.patch"),
titleRes = R.string.patch_player_backdrop_title, titleRes = R.string.patch_player_backdrop_title,
descRes = R.string.patch_player_backdrop_desc, descRes = R.string.patch_player_backdrop_desc,
), ),
CoverEverywhere(
order = 35,
fileNames = listOf(
"home-backdrop.patch",
"collection-backdrop.patch",
"cover-capture.patch",
),
titleRes = R.string.patch_cover_everywhere_title,
descRes = R.string.patch_cover_everywhere_desc,
),
DebugMenuUnlock( DebugMenuUnlock(
order = 100,
fileNames = listOf("debug-menu-unlock.patch"), fileNames = listOf("debug-menu-unlock.patch"),
titleRes = R.string.patch_debug_menu_unlock_title, titleRes = R.string.patch_debug_menu_unlock_title,
descRes = R.string.patch_debug_menu_unlock_desc, descRes = R.string.patch_debug_menu_unlock_desc,
), ),
LyricsProgressPill( LyricsProgressPill(
order = 40,
fileNames = listOf( fileNames = listOf(
"lyrics-progress-pill.patch", "lyrics-progress-pill.patch",
"lyrics-fade-region.patch", "lyrics-fade-region.patch",
), ),
titleRes = R.string.patch_lyrics_progress_pill_title, titleRes = R.string.patch_lyrics_progress_pill_title,
descRes = R.string.patch_lyrics_progress_pill_desc, descRes = R.string.patch_lyrics_progress_pill_desc,
requires = listOf(LyricsDisableCover, LyricsReplaceShareButton), requires = listOf(LyricsDisableCover, LyricsReplaceLyricsButton, LyricsReplaceShareButton),
), ),
EnableLegacyUi( EnableLegacyUi(
order = 10,
fileNames = listOf("enable-legacy-ui.patch"), fileNames = listOf("enable-legacy-ui.patch"),
titleRes = R.string.patch_enable_legacy_ui_title, titleRes = R.string.patch_enable_legacy_ui_title,
descRes = R.string.patch_enable_legacy_ui_desc, descRes = R.string.patch_enable_legacy_ui_desc,
@@ -57,15 +99,21 @@ enum class KnownPatch(
LyricsDisableCover, LyricsDisableCover,
LyricsReplaceLyricsButton, LyricsReplaceLyricsButton,
LyricsReplaceShareButton, LyricsReplaceShareButton,
LyricsRlApi,
LyricsKeepControlsVisible,
PlayerBackdrop, PlayerBackdrop,
LyricsProgressPill, LyricsProgressPill,
CoverEverywhere,
), ),
); );
companion object { companion object {
// Alphabetical by first filename, but pin DebugMenuUnlock to the bottom /**
* Sorted by `order` ascending. Tie-breaks fall back to the first filename
* (alphabetical) so the order is always deterministic.
*/
val All: List<KnownPatch> = entries.sortedWith( val All: List<KnownPatch> = entries.sortedWith(
compareBy({ it == DebugMenuUnlock }, { it.fileNames.first() }) compareBy({ it.order }, { it.fileNames.first() })
) )
} }
} }
@@ -55,6 +55,10 @@ class UpdaterViewModel(
showDialog = false showDialog = false
} }
fun reopenDialog() {
if (targetVersion != null) showDialog = true
}
fun triggerUpdate() = viewModelScope.launchIO { fun triggerUpdate() = viewModelScope.launchIO {
if (!isWorking.compareAndSet(expect = false, update = true)) if (!isWorking.compareAndSet(expect = false, update = true))
return@launchIO return@launchIO
@@ -5,11 +5,9 @@ import android.app.Activity
import android.content.* import android.content.*
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Resources import android.content.res.Resources
import android.net.ConnectivityManager
import android.net.Uri import android.net.Uri
import android.os.* import android.os.*
import android.provider.Settings import android.provider.Settings
import android.telephony.TelephonyManager
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.widget.Toast import android.widget.Toast
@@ -152,37 +150,6 @@ suspend fun Context.isPlayProtectEnabled(): Boolean? {
} }
} }
/**
* Check whether the device is connected on a metered WIFI connection or through any type of mobile data,
* to avoid unknowingly downloading a lot of stuff through a potentially metered network.
*/
@Suppress("DEPRECATION")
fun Context.isNetworkDangerous(): Boolean {
val connectivity = this.getSystemService<ConnectivityManager>()
?: error("Unable to get system connectivity service")
if (connectivity.isActiveNetworkMetered) return true
when (val info = connectivity.activeNetworkInfo) {
null -> return false
else -> {
if (info.isRoaming) return true
if (info.type == ConnectivityManager.TYPE_WIFI) return false
}
}
val telephony = this.getSystemService<TelephonyManager>()
?: error("Unable to get system telephony service")
val dangerousMobileDataStates = arrayOf(
/* TelephonyManager.DATA_DISCONNECTING */ 4,
TelephonyManager.DATA_CONNECTED,
TelephonyManager.DATA_CONNECTING,
)
return dangerousMobileDataStates.contains(telephony.dataState)
}
/** /**
* Gets the user associated with this context. * Gets the user associated with this context.
*/ */
@@ -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>
@@ -5,5 +5,5 @@
android:viewportHeight="960"> android:viewportHeight="960">
<path <path
android:fillColor="#e8eaed" 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> </vector>
+11 -9
View File
@@ -129,7 +129,6 @@
<string name="navigation_back">Back</string> <string name="navigation_back">Back</string>
<string name="navigation_about">About</string> <string name="navigation_about">About</string>
<string name="navigation_logs">Logs</string>
<string name="navigation_settings">Settings</string> <string name="navigation_settings">Settings</string>
<string name="navigation_refresh">Refresh</string> <string name="navigation_refresh">Refresh</string>
@@ -259,8 +258,18 @@
<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_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_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_replace_button_desc">Replaces the Lyrics button with the RL Sparkle!</string>
<string name="patch_lyrics_rl_api_title">Radiant Lyrics API</string>
<string name="patch_lyrics_rl_api_desc">Use Radiant Lyrics API to fetch Lyrics (Higher quality &amp; 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_title">Player Backdrop</string>
<string name="patch_player_backdrop_desc">Restores the legacy translucent backdrop blur behind the player.</string> <string name="patch_player_backdrop_desc">Restores the legacy translucent backdrop blur behind the player.</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 &amp; Collection)</string>
<string name="patch_lyrics_replace_share_button_title">Replace Share Button</string> <string name="patch_lyrics_replace_share_button_title">Replace Share Button</string>
<string <string
name="patch_lyrics_replace_share_button_desc" name="patch_lyrics_replace_share_button_desc"
@@ -293,13 +302,6 @@
<string name="log_action_export_apk">Export APK</string> <string name="log_action_export_apk">Export APK</string>
<string name="log_action_share">Share log</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_title">Active installation</string>
<string name="notif_group_install_desc">Progress notifications sent during a minimized 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> <string name="notif_install_ready_title">Radiant Lyrics installation ready!</string>
@@ -315,7 +317,7 @@
<string name="play_protect_warning_open_gpp">Open Play Protect</string> <string name="play_protect_warning_open_gpp">Open Play Protect</string>
<string name="play_protect_warning_disable">Don\'t show this again</string> <string name="play_protect_warning_disable">Don\'t show this again</string>
<string name="fun_fact_prefix">Fun Fact: %s</string> <string name="fun_fact_prefix">%s</string>
<string name="fun_fact_1">Did you know that TIDAL no longer like blur!</string> <string name="fun_fact_1">Did you know that TIDAL no longer like blur!</string>
<string name="fun_fact_2">Radiant Lyrics is also available for DESKTOP!!!</string> <string name="fun_fact_2">Radiant Lyrics is also available for DESKTOP!!!</string>
<string name="fun_fact_3">i am in your walls!</string> <string name="fun_fact_3">i am in your walls!</string>
+22
View File
@@ -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
+11
View File
@@ -0,0 +1,11 @@
--- a/com/tidal/android/feature/appscaffold/ui/p.smali
+++ b/com/tidal/android/feature/appscaffold/ui/p.smali
@@ -51,6 +51,8 @@
.line 10
iput-object p4, p0, Lcom/tidal/android/feature/appscaffold/ui/p;->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/p;->e:Z
+2 -2
View File
@@ -1,5 +1,5 @@
{ {
"tidalVersionCode": 9089, "tidalVersionCode": 9090,
"tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk", "tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk",
"patchesVersion": "0.6.0" "patchesVersion": "0.9.1"
} }
+13 -20
View File
@@ -1,43 +1,36 @@
--- a/com/tidal/android/core/debug/DebugFeatureInteractorDefault.smali --- a/com/tidal/android/core/debug/DebugFeatureInteractorDefault.smali
+++ b/com/tidal/android/core/debug/DebugFeatureInteractorDefault.smali +++ b/com/tidal/android/core/debug/DebugFeatureInteractorDefault.smali
@@ -53,6 +53,11 @@ @@ -54,6 +54,9 @@
.method public final a()Z .method public final a()Z
.locals 2 .locals 2
+ # rl-debug-unlock: force a() to always return true, bypassing the + const/4 v0, 0x1 # force true
+ # "debug-menu" feature flag check that normally hides the Settings entry. + return v0 # bypass debug flag
+ const/4 v0, 0x1
+ return v0
+ +
.line 1 .line 1
iget-object v0, p0, Lcom/tidal/android/core/debug/DebugFeatureInteractorDefault;->a:Lcom/tidal/android/featureflags/l; iget-object v0, p0, Lcom/tidal/android/core/debug/DebugFeatureInteractorDefault;->a:Lcom/tidal/android/featureflags/l;
@@ -97,6 +102,14 @@ @@ -98,6 +101,14 @@
} }
.end annotation .end annotation
+ # rl-debug-unlock: short-circuit b() to always emit MutableStateFlow(TRUE). + sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean; # always-true value
+ # Mirrors the original ":cond_1 :goto_0" path the app uses for internal/test builds.
+ sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;
+ +
+ invoke-static {v0}, Lkotlinx/coroutines/flow/StateFlowKt;->MutableStateFlow(Ljava/lang/Object;)Lkotlinx/coroutines/flow/MutableStateFlow; + invoke-static {v0}, Lkotlinx/coroutines/flow/StateFlowKt;->MutableStateFlow(Ljava/lang/Object;)Lkotlinx/coroutines/flow/MutableStateFlow; # wrap in flow
+ +
+ move-result-object v0 + move-result-object v0 # the flow
+ +
+ return-object v0 + return-object v0 # short-circuit b()
+ +
.line 1 .line 1
sget-object v0, Lh50/a;->a:Ljava/lang/String; sget-object v0, Lj50/a;->a:Ljava/lang/String;
@@ -261,6 +274,11 @@ @@ -261,5 +272,8 @@
.method public final c()Z .method public final c()Z
.locals 3 .locals 3
+ # rl-debug-unlock: force c() to always return true, bypassing the + const/4 v0, 0x1 # force true
+ # applicationId-substring / in-app-bug-reports / export-logs flag checks. + return v0 # bypass debug flag
+ const/4 v0, 0x1
+ return v0
+ +
.line 1 .line 1
sget-object v0, Lh50/a;->a:Ljava/lang/String; sget-object v0, Lj50/a;->a:Ljava/lang/String;
+3 -3
View File
@@ -1,7 +1,7 @@
--- a/ig/d.smali --- a/kg/e.smali
+++ b/ig/d.smali +++ b/kg/e.smali
@@ -26,8 +26,8 @@ @@ -26,8 +26,8 @@
new-instance v0, Lig/d; new-instance v0, Lkg/e;
.line 2 .line 2
.line 3 .line 3
@@ -0,0 +1,370 @@
.class public final Lradiant/HomeBackdrop;
.super Ljava/lang/Object;
# static fields
.field public static volatile currentAlbumId:I
.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, 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 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
sget v2, Lradiant/HomeBackdrop;->currentAlbumId:I
new-instance v3, Lcom/tidal/android/feature/playerscreen/ui/composables/n0;
invoke-direct {v3, v2, v1}, Lcom/tidal/android/feature/playerscreen/ui/composables/n0;-><init>(ILjava/lang/String;)V
sget-object v4, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion;
const/4 v5, 0x0
const/4 v6, 0x1
const/4 v7, 0x0
invoke-static {v4, v5, v6, v7}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxSize$default(Landroidx/compose/ui/Modifier;FILjava/lang/Object;)Landroidx/compose/ui/Modifier;
move-result-object v4
const/high16 v5, 0x42200000
invoke-static {v5}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F
move-result v5
sget-object v6, Landroidx/compose/ui/draw/BlurredEdgeTreatment;->Companion:Landroidx/compose/ui/draw/BlurredEdgeTreatment$Companion;
invoke-virtual {v6}, Landroidx/compose/ui/draw/BlurredEdgeTreatment$Companion;->getRectangle---Goahg()Landroidx/compose/ui/graphics/Shape;
move-result-object v6
invoke-static {v4, v5, v6}, Landroidx/compose/ui/draw/BlurKt;->blur-F8QBwvs(Landroidx/compose/ui/Modifier;FLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier;
move-result-object v4
sget-object v5, Landroidx/compose/ui/layout/ContentScale;->Companion:Landroidx/compose/ui/layout/ContentScale$Companion;
invoke-virtual {v5}, Landroidx/compose/ui/layout/ContentScale$Companion;->getCrop()Landroidx/compose/ui/layout/ContentScale;
move-result-object v5
move-object/from16 v6, v3
const/4 v7, 0x0
move-object/from16 v8, v4
const/4 v9, 0x0
move-object/from16 v10, v5
move-object/from16 v11, v1 # uuid as cache key
const/4 v12, 0x0
move-object/from16 v13, v0
const/4 v14, 0x0
const/16 v15, 0x48
invoke-static/range {v6 .. v15}, Lxd0/f;->a(Lyl0/l;Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/graphics/ColorFilter;Landroidx/compose/ui/layout/ContentScale;Ljava/lang/Object;Lyl0/a;Landroidx/compose/runtime/Composer;II)V
sget-object v3, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion;
const/4 v4, 0x0
const/4 v5, 0x1
const/4 v6, 0x0
invoke-static {v3, v4, v5, v6}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxSize$default(Landroidx/compose/ui/Modifier;FILjava/lang/Object;)Landroidx/compose/ui/Modifier;
move-result-object v3
const v4, -0x80000000
invoke-static {v4}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J
move-result-wide v4
invoke-static {}, Landroidx/compose/ui/graphics/RectangleShapeKt;->getRectangleShape()Landroidx/compose/ui/graphics/Shape;
move-result-object v6
invoke-static {v3, v4, v5, v6}, Landroidx/compose/foundation/BackgroundKt;->background-bw27NRU(Landroidx/compose/ui/Modifier;JLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier;
move-result-object v3
const/4 v4, 0x0
invoke-static {v3, v0, v4}, Landroidx/compose/foundation/layout/SpacerKt;->Spacer(Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V
:skip
invoke-interface {v0}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V
return-void
.end method
+1 -1
View File
@@ -1,6 +1,6 @@
.class public final Lradiant/NoOp; .class public final Lradiant/NoOp;
.super Ljava/lang/Object; .super Ljava/lang/Object;
.implements Ltl0/a; .implements Lyl0/a;
# static fields # static fields
@@ -0,0 +1,212 @@
.class public final Lradiant/RLAPILyricsHook;
.super Ljava/lang/Object;
# static fields
.field public static volatile currentKey:Ljava/lang/String;
.field public static volatile isRlState:Z
# direct methods
.method static constructor <clinit>()V
.locals 1
const/4 v0, 0x0
sput-object v0, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
sput-boolean v0, Lradiant/RLAPILyricsHook;->isRlState: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 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/4 v3, 0x0
sput-boolean v3, Lradiant/RLAPILyricsHook;->isRlState:Z
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
return-void
: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
return-void
: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
return-void
:title_ok
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: getArtistNames() returned null"
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
return-void
: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
return-void
:artist_ok
const-string v3, ""
new-instance v4, Ljava/lang/StringBuilder;
invoke-direct {v4}, Ljava/lang/StringBuilder;-><init>()V
invoke-virtual {v4, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
const-string v5, "|"
invoke-virtual {v4, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
invoke-virtual {v4, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v4
sput-object v4, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
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
:done
return-void
.end method
@@ -0,0 +1,545 @@
.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;
# 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 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
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"
const-string v2, "null"
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
const-string v5, "text"
const-string v6, ""
invoke-virtual {v4, v5, v6}, Lorg/json/JSONObject;->optString(Ljava/lang/String;Ljava/lang/String;)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
.method private runImpl()V
.locals 11
iget-object v0, p0, Lradiant/RLAPILyricsWorker;->title:Ljava/lang/String;
const-string v1, "UTF-8"
invoke-static {v0, v1}, Ljava/net/URLEncoder;->encode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
iget-object v2, p0, Lradiant/RLAPILyricsWorker;->artist:Ljava/lang/String;
invoke-static {v2, v1}, Ljava/net/URLEncoder;->encode(Ljava/lang/String;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;
const-string v0, "&platform=Radiant%20Lyrics"
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
sget-boolean v4, Lradiant/StickyLyrics;->enabled:Z
if-nez v4, :no_close
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;->N:Lkotlinx/coroutines/flow/MutableStateFlow;
sget-object v6, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;
invoke-interface {v5, v6}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
:no_close
return-void
:got_body
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
return-void
: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
return-void
: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
iget-object v4, p0, Lradiant/RLAPILyricsWorker;->key:Ljava/lang/String;
sget-object v5, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
invoke-virtual {v4, v5}, Ljava/lang/String;->equals(Ljava/lang/Object;)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
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;->M:Lkotlinx/coroutines/flow/MutableStateFlow;
invoke-static {v3}, Ltn0/a;->c(Ljava/lang/Iterable;)Ltn0/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;Ltn0/b;IZ)V
const-string v6, "publishing g$c -> J=true N=true M=g$c"
invoke-static {v6}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
const/4 v6, 0x1
sput-boolean v6, Lradiant/RLAPILyricsHook;->isRlState:Z
iget-object v6, v4, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->J: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/StickyLyrics;->enabled:Z
if-eqz v9, :skip_n
iget-object v6, v4, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->N:Lkotlinx/coroutines/flow/MutableStateFlow;
invoke-interface {v6, v7}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
:skip_n
invoke-interface {v5, v8}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V
:done
return-void
.end method
# virtual methods
.method public run()V
.locals 1
: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
return-void
.end method
@@ -4,7 +4,7 @@
# direct methods # direct methods
.method public static final a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Ltl0/a;)V .method public static final a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lyl0/a;)V
.locals 21 .locals 21
.annotation build Landroidx/compose/runtime/Composable; .annotation build Landroidx/compose/runtime/Composable;
.end annotation .end annotation
@@ -98,7 +98,7 @@
const/4 v4, 0x6 const/4 v4, 0x6
invoke-static {v2, v11, v4}, Lcom/squareup/ui/market/core/theme/w;->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/x;->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 move-result-object v15
@@ -131,7 +131,7 @@
const/16 v18, 0x0 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;)Ll20/v1; 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;
move-result-object v4 move-result-object v4
@@ -140,7 +140,7 @@
:cond_5 :cond_5
move-object v9, v4 move-object v9, v4
check-cast v9, Ll20/v1; check-cast v9, Ln20/w1;
sget v2, Lcom/tidal/android/feature/playerscreen/ui/R$string;->lyrics:I sget v2, Lcom/tidal/android/feature/playerscreen/ui/R$string;->lyrics:I
@@ -172,7 +172,7 @@
const/4 v8, 0x0 const/4 v8, 0x0
invoke-static/range {v1 .. v13}, Lcom/squareup/ui/market/components/MarketIconButtonKt;->c(Ltl0/a;Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/interaction/MutableInteractionSource;ZLcom/squareup/ui/market/components/n;Ltl0/a;Ljava/lang/String;Ll20/v1;Ltl0/p;Landroidx/compose/runtime/Composer;II)V 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 {}, Landroidx/compose/runtime/ComposerKt;->isTraceInProgress()Z invoke-static {}, Landroidx/compose/runtime/ComposerKt;->isTraceInProgress()Z
@@ -3,7 +3,7 @@
.source "SourceFile" .source "SourceFile"
# interfaces # interfaces
.implements Ltl0/p; .implements Lyl0/p;
# virtual methods # virtual methods
+1 -1
View File
@@ -1,6 +1,6 @@
.class public final Lradiant/SpvFactory; .class public final Lradiant/SpvFactory;
.super Ljava/lang/Object; .super Ljava/lang/Object;
.implements Ltl0/l; .implements Lyl0/l;
# static fields # 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
+43
View File
@@ -0,0 +1,43 @@
--- 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/p.smali
+++ b/com/tidal/android/feature/home/ui/p.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/e.smali
+++ b/com/tidal/android/feature/home/ui/composables/e.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;
+9 -9
View File
@@ -1,26 +1,26 @@
--- a/com/tidal/android/feature/playerscreen/ui/g0.smali --- a/com/tidal/android/feature/playerscreen/ui/h0.smali
+++ b/com/tidal/android/feature/playerscreen/ui/g0.smali +++ b/com/tidal/android/feature/playerscreen/ui/h0.smali
@@ -666,8 +666,23 @@ @@ -669,8 +669,23 @@
move-object v5, v1 move-object v5, v1
.line 288 .line 289
+ const v1, 0x52414443 # group key + const v1, 0x52414443 # group key
+ +
+ invoke-interface {v7, v1}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open group around the cover + 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/g0;->b:Lcom/tidal/android/feature/playerscreen/ui/r$a; # player state + 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, 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/r$a;->j:Lcom/tidal/android/feature/playerscreen/ui/g; # current view mode
+ +
+ instance-of v1, v1, Lcom/tidal/android/feature/playerscreen/ui/g$a; # only render when on cover 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 + 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;Ltl0/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;Lyl0/l;FLandroidx/compose/ui/Modifier;ZLandroidx/compose/runtime/Composer;II)V
+ :radiant_after_cover + :radiant_after_cover # skip target
+ invoke-interface {v7}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close group + invoke-interface {v7}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close group
+ +
.line 289
.line 290 .line 290
.line 291 .line 291
.line 292
+31 -5
View File
@@ -1,11 +1,37 @@
--- a/com/tidal/android/feature/playerscreen/ui/composables/k1.smali --- a/com/tidal/android/feature/playerscreen/ui/composables/o1.smali
+++ b/com/tidal/android/feature/playerscreen/ui/composables/k1.smali +++ b/com/tidal/android/feature/playerscreen/ui/composables/o1.smali
@@ -64,7 +64,7 @@ @@ -64,26 +64,29 @@
.line 16 .line 16
.line 17 .line 17
- iget v2, p0, Lcom/tidal/android/feature/playerscreen/ui/composables/k1;->a:F - iget v2, p0, Lcom/tidal/android/feature/playerscreen/ui/composables/o1;->a:F
+ const/high16 v2, 0x43480000 # hardcode top fade region to 200dp (decouple from contentPadding) + const/high16 v2, 0x43200000 # 160f start dp
.line 18 .line 18
.line 19 .line 19
invoke-interface {p1, v2}, Landroidx/compose/ui/unit/Density;->toPx-0680j_4(F)F
.line 20
.line 21
.line 22
- move-result v3
+ move-result v2 # startY in px
+
+ const/high16 v3, 0x435c0000 # 220f end dp
+
+ 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
.line 25
const/4 v6, 0x0
- .line 26
- const/4 v2, 0x0
-
.line 27
const/4 v4, 0x0
@@ -0,0 +1,15 @@
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt$PlayerScreenPortrait$2$1.smali
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt$PlayerScreenPortrait$2$1.smali
@@ -228,11 +228,11 @@
.line 23
.line 24
iget-boolean p1, p0, Lcom/tidal/android/feature/playerscreen/ui/PlayerScreenKt$PlayerScreenPortrait$2$1;->$isLyricsVisible:Z
.line 25
.line 26
- if-eqz p1, :cond_3
+ goto :cond_3 # skip auto-hide branch
.line 27
.line 28
iput v2, p0, Lcom/tidal/android/feature/playerscreen/ui/PlayerScreenKt$PlayerScreenPortrait$2$1;->label:I
+7 -7
View File
@@ -1,6 +1,6 @@
--- a/com/tidal/android/feature/playerscreen/ui/b0.smali --- a/com/tidal/android/feature/playerscreen/ui/c0.smali
+++ b/com/tidal/android/feature/playerscreen/ui/b0.smali +++ b/com/tidal/android/feature/playerscreen/ui/c0.smali
@@ -45,7 +45,7 @@ @@ -47,7 +47,7 @@
# virtual methods # virtual methods
.method public final invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; .method public final invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
@@ -9,9 +9,9 @@
.line 1 .line 1
move-object/from16 v0, p0 move-object/from16 v0, p0
@@ -460,6 +460,64 @@ @@ -461,6 +461,64 @@
.line 200 .line 200
invoke-static/range {v2 .. v9}, Lcom/tidal/android/feature/playerscreen/ui/composables/LyricsKt;->a(Lcom/tidal/android/feature/playerscreen/ui/g;Ltl0/l;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/PaddingValues;Ltl0/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;Lyl0/l;Landroidx/compose/ui/Modifier;Landroidx/compose/foundation/layout/PaddingValues;Lyl0/l;Landroidx/compose/runtime/Composer;II)V
+ sget-object v17, Lradiant/SpvFactory;->a:Lradiant/SpvFactory; # progress view factory + sget-object v17, Lradiant/SpvFactory;->a:Lradiant/SpvFactory; # progress view factory
+ +
@@ -29,7 +29,7 @@
+ +
+ const/16 v24, 0x0 # synthetic null + 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;Ltl0/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;Lyl0/a;ILjava/lang/Object;)Landroidx/compose/ui/Modifier; # apply clickable
+ +
+ move-result-object v23 # clickable modifier + move-result-object v23 # clickable modifier
+ +
@@ -69,7 +69,7 @@
+ +
+ const/16 v22, 0x4 # default mask + const/16 v22, 0x4 # default mask
+ +
+ invoke-static/range {v17 .. v22}, Landroidx/compose/ui/viewinterop/AndroidView_androidKt;->AndroidView(Ltl0/l;Landroidx/compose/ui/Modifier;Ltl0/l;Landroidx/compose/runtime/Composer;II)V # mount progress pill + 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
+ +
.line 201 .line 201
.line 202 .line 202
+11 -11
View File
@@ -1,11 +1,11 @@
# rl-locals: com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali e( 79 # rl-locals: com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali e( 79
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali --- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali +++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
@@ -4931,7 +4931,11 @@ @@ -4945,7 +4945,11 @@
const/4 v10, 0x0 const/4 v7, 0x0
.line 226 .line 226
- invoke-static {v10, v9, v4, v2, v7}, Lcom/tidal/android/feature/playerscreen/ui/composables/h1;->a(Landroidx/compose/ui/Modifier;Ltl0/a;ZLandroidx/compose/runtime/Composer;I)V - 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 + const v10, 0x52414448 # empty group key
+ +
+ invoke-interface {v2, v10}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty + invoke-interface {v2, v10}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty
@@ -14,21 +14,21 @@
.line 227 .line 227
invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V
@@ -5838,6 +5838,22 @@ @@ -5720,6 +5724,22 @@
:cond_51 :cond_51
check-cast v4, Ltl0/a; check-cast v4, Lyl0/a;
const/4 v2, 0x0 const/4 v2, 0x0
invoke-static {v13, v7, v2, v4}, Lcom/tidal/android/feature/playerscreen/ui/composables/h3;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Ltl0/a;)V 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
+ +
+ new-instance v74, Lc8/j; # lyrics-toggle lambda + new-instance v74, Lcom/tidal/android/feature/playerscreen/ui/g0; # lyrics toggle lambda
+ +
+ move-object/from16 v75, p5 # lambda receiver + move-object/from16 v75, p5 # action dispatcher
+ +
+ const/16 v76, 0x1 # lyrics action + const/16 v76, 0x0 # lyrics action disc
+ +
+ invoke-direct/range {v74 .. v76}, Lc8/j;-><init>(Ljava/lang/Object;I)V # build lambda + invoke-direct/range {v74 .. v76}, Lcom/tidal/android/feature/playerscreen/ui/g0;-><init>(Ljava/lang/Object;I)V # build lambda
+ +
+ const/16 v71, 0x0 # changed flags + const/16 v71, 0x0 # changed flags
+ +
@@ -36,4 +36,4 @@
+ +
+ const/16 v73, 0x0 # null modifier + const/16 v73, 0x0 # null modifier
+ +
+ invoke-static/range {v71 .. v74}, Lradiant/SparkleButton;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Ltl0/a;)V # render sparkle button + invoke-static/range {v71 .. v74}, Lradiant/SparkleButton;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lyl0/a;)V # render sparkle button
+14 -15
View File
@@ -1,10 +1,10 @@
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali --- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali +++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
@@ -5086,7 +5086,11 @@ @@ -5094,7 +5094,11 @@
const/4 v13, 0x0 const/4 v13, 0x0
.line 247 .line 247
- invoke-static {v9, v0, v13, v2, v10}, Lcom/tidal/android/feature/playerscreen/ui/composables/BroadcastButtonKt;->b(Lcom/tidal/android/feature/playerscreen/ui/b;Ltl0/a;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V - 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 + const v9, 0x52414244 # empty group key
+ +
+ invoke-interface {v2, v9}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty + invoke-interface {v2, v9}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty
@@ -13,39 +13,38 @@
.line 248 .line 248
invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endNode()V invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endNode()V
@@ -5758,17 +5758,19 @@ @@ -5770,16 +5774,20 @@
.line 335
:cond_53 :cond_53
- new-instance v4, Landroidx/compose/foundation/text/input/internal/selection/l; - new-instance v4, Lcom/aspiro/wamp/tidalconnect/playback/i;
- -
- const/4 v6, 0x2 - invoke-direct {v4, v11, v1}, Lcom/aspiro/wamp/tidalconnect/playback/i;-><init>(Ljava/lang/Object;I)V
-
- invoke-direct {v4, v11, v6}, Landroidx/compose/foundation/text/input/internal/selection/l;-><init>(Ljava/lang/Object;I)V
- -
- .line 336 - .line 336
- invoke-interface {v7, v4}, Landroidx/compose/runtime/Composer;->updateRememberedValue(Ljava/lang/Object;)V - invoke-interface {v7, v4}, Landroidx/compose/runtime/Composer;->updateRememberedValue(Ljava/lang/Object;)V
- -
- .line 337 - .line 337
- :cond_54 - :cond_54
- check-cast v4, Ltl0/a; - check-cast v4, Lyl0/a;
- -
- const/4 v2, 0x0 - const/4 v2, 0x0
- -
- invoke-static {v13, v7, v2, v4}, Lcom/tidal/android/feature/playerscreen/ui/composables/u4;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Ltl0/a;)V - 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/tidal/android/feature/playerscreen/ui/f0; # connect click lambda + new-instance v4, Lcom/aspiro/wamp/tidalconnect/playback/i; # connect click lambda factory
+ +
+ move-object/from16 v6, p5 # action dispatcher + move-object/from16 v6, p5 # action dispatcher
+ +
+ const/4 v8, 0x0 # connect-clicked discriminator + const/4 v8, 0x1 # connect-clicked disc
+ +
+ invoke-direct {v4, v6, v8}, Lcom/tidal/android/feature/playerscreen/ui/f0;-><init>(Ljava/lang/Object;I)V # build lambda + 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 + invoke-interface {v7, v4}, Landroidx/compose/runtime/Composer;->updateRememberedValue(Ljava/lang/Object;)V # cache lambda
+ +
+ :cond_54 + :cond_54 # cache join
+ check-cast v4, Ltl0/a; + 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 + 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 + 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;Ltl0/a;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V # render connect button + 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
+17
View File
@@ -0,0 +1,17 @@
--- a/com/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a.smali
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a.smali
@@ -190,7 +190,14 @@
goto :goto_0
.line 67
:cond_3
+ sget-boolean v6, Lradiant/RLAPILyricsHook;->isRlState:Z # read RL flag
+
+ if-eqz v6, :radiant_skip # skip if not RL
+
+ const/4 v5, -0x1 # RL no-match default = -1
+
+ :radiant_skip
if-gez v4, :cond_4
.line 68
+21
View File
@@ -0,0 +1,21 @@
--- a/com/tidal/android/feature/playerscreen/ui/PlayerViewModel.smali
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerViewModel.smali
@@ -1277,7 +1277,9 @@
.line 104
.line 105
check-cast v1, Lcom/aspiro/wamp/model/Track;
+ invoke-static {v0, v1}, Lradiant/RLAPILyricsHook;->onWampTrack(Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;Lcom/aspiro/wamp/model/Track;)V # RL API lyrics hook
+
.line 106
.line 107
invoke-virtual {v1}, Lcom/aspiro/wamp/model/MediaItem;->getId()I
@@ -1551,7 +1553,7 @@
.line 235
.line 236
.line 237
- if-nez v9, :cond_a
+ goto :cond_a # skip TIDAL N=false setter
.line 238
.line 239
@@ -1,7 +1,7 @@
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali --- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali +++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
@@ -5854,17 +5854,38 @@ @@ -5725,17 +5725,38 @@
invoke-static {v13, v7, v2, v4}, Lcom/tidal/android/feature/playerscreen/ui/composables/h3;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Ltl0/a;)V 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
+ +
+ iget-boolean v2, v10, Lcom/tidal/android/feature/playerscreen/ui/r$a;->i:Z # hasLyrics flag + iget-boolean v2, v10, Lcom/tidal/android/feature/playerscreen/ui/r$a;->i:Z # hasLyrics flag
+ +
@@ -11,13 +11,13 @@
+ +
+ invoke-interface {v7, v2}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open lyrics branch + invoke-interface {v7, v2}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open lyrics branch
new-instance v74, Lc8/j; # lyrics-toggle lambda new-instance v74, Lcom/tidal/android/feature/playerscreen/ui/g0; # lyrics toggle lambda
move-object/from16 v75, p5 # lambda receiver move-object/from16 v75, p5 # action dispatcher
const/16 v76, 0x1 # lyrics action const/16 v76, 0x0 # lyrics action disc
invoke-direct/range {v74 .. v76}, Lc8/j;-><init>(Ljava/lang/Object;I)V # build lambda invoke-direct/range {v74 .. v76}, Lcom/tidal/android/feature/playerscreen/ui/g0;-><init>(Ljava/lang/Object;I)V # build lambda
const/16 v71, 0x0 # changed flags const/16 v71, 0x0 # changed flags
@@ -25,17 +25,17 @@
const/16 v73, 0x0 # null modifier const/16 v73, 0x0 # null modifier
invoke-static/range {v71 .. v74}, Lradiant/SparkleButton;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Ltl0/a;)V # render sparkle button invoke-static/range {v71 .. v74}, Lradiant/SparkleButton;->a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lyl0/a;)V # render sparkle button
+ +
+ invoke-interface {v7}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close lyrics branch + invoke-interface {v7}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close lyrics branch
+ +
+ goto :goto_sparkle_done # skip empty branch + goto :goto_sparkle_done # skip empty branch
+ +
+ :cond_sparkle_no_lyrics + :cond_sparkle_no_lyrics # no-lyrics branch
+ const v2, 0x3057f75c # group key (no lyrics) + const v2, 0x3057f75c # group key (no lyrics)
+ +
+ invoke-interface {v7, v2}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty + invoke-interface {v7, v2}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty
+ +
+ invoke-interface {v7}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close empty + invoke-interface {v7}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close empty
+ +
+ :goto_sparkle_done + :goto_sparkle_done # join target
@@ -0,0 +1,32 @@
--- a/com/tidal/android/feature/playerscreen/ui/PlayerViewModel.smali
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerViewModel.smali
@@ -1060,9 +1060,13 @@
move-object/from16 v0, p0
.line 2
.line 3
move-object/from16 v1, p1
+ const/4 v2, 0x1 # true literal
+
+ sput-boolean v2, Lradiant/StickyLyrics;->enabled:Z # arm sticky flag
+
.line 4
.line 5
invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
@@ -1568,9 +1572,15 @@
.line 244
.line 245
.line 246
:cond_a
+ iget-object v9, v0, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->N:Lkotlinx/coroutines/flow/MutableStateFlow; # N flow
+
+ sget-object v10, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean; # TRUE literal
+
+ invoke-interface {v9, v10}, Lkotlinx/coroutines/flow/MutableStateFlow;->setValue(Ljava/lang/Object;)V # force N=true
+
iget-object v9, v0, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->K:Lkotlinx/coroutines/flow/MutableStateFlow;
.line 247
.line 248
iget-object v10, v1, Lcom/tidal/android/tidalapi/domain/model/o;->j:Lcom/tidal/android/tidalapi/domain/model/a;
+42 -42
View File
@@ -1,11 +1,11 @@
# rl-locals: com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali e( 71 # rl-locals: com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali e( 71
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali --- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali +++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
@@ -4164,6 +4164,133 @@ @@ -4172,6 +4172,133 @@
invoke-static {v5, v3, v4}, Landroidx/compose/runtime/Updater;->set-impl(Landroidx/compose/runtime/Composer;Ljava/lang/Object;Ltl0/p;)V invoke-static {v5, v3, v4}, Landroidx/compose/runtime/Updater;->set-impl(Landroidx/compose/runtime/Composer;Ljava/lang/Object;Lyl0/p;)V
+ const v3, 0x52414449 # group key for slot table + const v3, 0x52414449 # slot table key
+ +
+ invoke-interface {v10, v3}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open group + invoke-interface {v10, v3}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open group
+ +
@@ -13,75 +13,75 @@
+ +
+ iget-object v3, v3, Lcom/tidal/android/feature/playerscreen/ui/r$a;->c:Lcom/tidal/android/feature/playerscreen/ui/d; # cover pager + iget-object v3, v3, Lcom/tidal/android/feature/playerscreen/ui/r$a;->c:Lcom/tidal/android/feature/playerscreen/ui/d; # cover pager
+ +
+ iget-object v4, v3, Lcom/tidal/android/feature/playerscreen/ui/d;->a:Lon0/b; # item list + iget-object v4, v3, Lcom/tidal/android/feature/playerscreen/ui/d;->a:Ltn0/b; # item list
+ +
+ iget v5, v3, Lcom/tidal/android/feature/playerscreen/ui/d;->b:I # current index + iget v5, v3, Lcom/tidal/android/feature/playerscreen/ui/d;->b:I # current index
+ +
+ invoke-interface {v4}, Ljava/util/List;->size()I + invoke-interface {v4}, Ljava/util/List;->size()I # list size
+ +
+ move-result v6 + move-result v6 # size value
+ +
+ if-le v6, v5, :radiant_skip # index out of bounds -> skip + if-le v6, v5, :radiant_skip # bounds check
+ +
+ if-ltz v5, :radiant_skip + if-ltz v5, :radiant_skip # negative check
+ +
+ invoke-interface {v4, v5}, Ljava/util/List;->get(I)Ljava/lang/Object; + invoke-interface {v4, v5}, Ljava/util/List;->get(I)Ljava/lang/Object; # current item
+ +
+ move-result-object v4 + move-result-object v4 # current item
+ +
+ instance-of v6, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a; # only album covers + instance-of v6, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a; # only album covers
+ +
+ if-eqz v6, :radiant_skip + if-eqz v6, :radiant_skip # skip non-albums
+ +
+ check-cast v4, Lcom/tidal/android/feature/playerscreen/ui/c$a; + check-cast v4, Lcom/tidal/android/feature/playerscreen/ui/c$a; # narrow type
+ +
+ iget v5, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a;->b:I # album id + iget v5, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a;->b:I # album id
+ +
+ iget-object v4, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a;->c:Ljava/lang/String; # cover uuid + iget-object v4, v4, Lcom/tidal/android/feature/playerscreen/ui/c$a;->c:Ljava/lang/String; # cover uuid
+ +
+ new-instance v6, Lcom/tidal/android/feature/playerscreen/ui/composables/p0; # tidal's cover request + new-instance v6, Lcom/tidal/android/feature/playerscreen/ui/composables/n0; # cover request lambda
+ +
+ invoke-direct {v6, v5, v4}, Lcom/tidal/android/feature/playerscreen/ui/composables/p0;-><init>(ILjava/lang/String;)V + invoke-direct {v6, v5, v4}, Lcom/tidal/android/feature/playerscreen/ui/composables/n0;-><init>(ILjava/lang/String;)V # build request
+ +
+ sget-object v5, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion; + sget-object v5, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion; # base modifier
+ +
+ const/4 v7, 0x0 + const/4 v7, 0x0 # fraction unused
+ +
+ const/4 v8, 0x1 + const/4 v8, 0x1 # default fraction
+ +
+ const/4 v3, 0x0 + const/4 v3, 0x0 # synthetic null
+ +
+ invoke-static {v5, v7, v8, v3}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxSize$default(Landroidx/compose/ui/Modifier;FILjava/lang/Object;)Landroidx/compose/ui/Modifier; # fill the player root + invoke-static {v5, v7, v8, v3}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxSize$default(Landroidx/compose/ui/Modifier;FILjava/lang/Object;)Landroidx/compose/ui/Modifier; # fill the player root
+ +
+ move-result-object v5 + move-result-object v5 # filled modifier
+ +
+ const/high16 v7, 0x42b40000 # 90f (blur radius dp) + const/high16 v7, 0x42b40000 # 90f blur dp
+ +
+ invoke-static {v7}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F + invoke-static {v7}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F # to Dp
+ +
+ move-result v7 + move-result v7 # blur dp value
+ +
+ sget-object v8, Landroidx/compose/ui/draw/BlurredEdgeTreatment;->Companion:Landroidx/compose/ui/draw/BlurredEdgeTreatment$Companion; + sget-object v8, Landroidx/compose/ui/draw/BlurredEdgeTreatment;->Companion:Landroidx/compose/ui/draw/BlurredEdgeTreatment$Companion; # blur edge companion
+ +
+ invoke-virtual {v8}, Landroidx/compose/ui/draw/BlurredEdgeTreatment$Companion;->getRectangle---Goahg()Landroidx/compose/ui/graphics/Shape; + invoke-virtual {v8}, Landroidx/compose/ui/draw/BlurredEdgeTreatment$Companion;->getRectangle---Goahg()Landroidx/compose/ui/graphics/Shape; # rectangle treatment
+ +
+ move-result-object v8 + move-result-object v8 # edge shape
+ +
+ invoke-static {v5, v7, v8}, Landroidx/compose/ui/draw/BlurKt;->blur-F8QBwvs(Landroidx/compose/ui/Modifier;FLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier; # apply blur + invoke-static {v5, v7, v8}, Landroidx/compose/ui/draw/BlurKt;->blur-F8QBwvs(Landroidx/compose/ui/Modifier;FLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier; # apply blur
+ +
+ move-result-object v5 + move-result-object v5 # blurred modifier
+ +
+ sget-object v7, Landroidx/compose/ui/layout/ContentScale;->Companion:Landroidx/compose/ui/layout/ContentScale$Companion; + sget-object v7, Landroidx/compose/ui/layout/ContentScale;->Companion:Landroidx/compose/ui/layout/ContentScale$Companion; # scale companion
+ +
+ invoke-virtual {v7}, Landroidx/compose/ui/layout/ContentScale$Companion;->getCrop()Landroidx/compose/ui/layout/ContentScale; # cover-crop scaling + invoke-virtual {v7}, Landroidx/compose/ui/layout/ContentScale$Companion;->getCrop()Landroidx/compose/ui/layout/ContentScale; # cover-crop scaling
+ +
+ move-result-object v7 + move-result-object v7 # crop scale
+ +
+ move-object/from16 v61, v6 # request + move-object/from16 v61, v6 # request
+ +
+ const/16 v62, 0x0 # contentDescription + const/16 v62, 0x0 # contentDescription
+ +
+ move-object/from16 v63, v5 # modifier (blurred + fillMaxSize) + move-object/from16 v63, v5 # blurred modifier
+ +
+ const/16 v64, 0x0 # colorFilter + const/16 v64, 0x0 # colorFilter
+ +
@@ -89,47 +89,47 @@
+ +
+ move-object/from16 v66, v4 # cover uuid + move-object/from16 v66, v4 # cover uuid
+ +
+ const/16 v67, 0x0 + const/16 v67, 0x0 # null onError
+ +
+ move-object/from16 v68, v10 # composer + move-object/from16 v68, v10 # composer
+ +
+ const/16 v69, 0x0 + const/16 v69, 0x0 # changed flags
+ +
+ const/16 v70, 0x48 + const/16 v70, 0x48 # default mask
+ +
+ invoke-static/range {v61 .. v70}, Lsd0/f;->a(Ltl0/l;Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/graphics/ColorFilter;Landroidx/compose/ui/layout/ContentScale;Ljava/lang/Object;Ltl0/a;Landroidx/compose/runtime/Composer;II)V # render blurred cover + invoke-static/range {v61 .. v70}, Lxd0/f;->a(Lyl0/l;Ljava/lang/String;Landroidx/compose/ui/Modifier;Landroidx/compose/ui/graphics/ColorFilter;Landroidx/compose/ui/layout/ContentScale;Ljava/lang/Object;Lyl0/a;Landroidx/compose/runtime/Composer;II)V # render blurred cover
+ +
+ sget-object v3, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion; # scrim chain start + sget-object v3, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion; # scrim chain start
+ +
+ const/4 v4, 0x0 # fraction unused + const/4 v4, 0x0 # fraction unused
+ +
+ const/4 v5, 0x1 # $default mask + const/4 v5, 0x1 # default mask
+ +
+ const/4 v6, 0x0 # null obj + const/4 v6, 0x0 # null obj
+ +
+ invoke-static {v3, v4, v5, v6}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxSize$default(Landroidx/compose/ui/Modifier;FILjava/lang/Object;)Landroidx/compose/ui/Modifier; # fill screen + invoke-static {v3, v4, v5, v6}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxSize$default(Landroidx/compose/ui/Modifier;FILjava/lang/Object;)Landroidx/compose/ui/Modifier; # fill screen
+ +
+ move-result-object v3 # modifier + move-result-object v3 # fullscreen modifier
+ +
+ const v6, -0x80000000 # 0x80000000 = 50% black + const v6, -0x80000000 # 50% black ARGB
+ +
+ invoke-static {v6}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack ARGB long + invoke-static {v6}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack color long
+ +
+ move-result-wide v6 # color + move-result-wide v6 # color long
+ +
+ invoke-static {}, Landroidx/compose/ui/graphics/RectangleShapeKt;->getRectangleShape()Landroidx/compose/ui/graphics/Shape; # rect shape + invoke-static {}, Landroidx/compose/ui/graphics/RectangleShapeKt;->getRectangleShape()Landroidx/compose/ui/graphics/Shape; # rect shape
+ +
+ move-result-object v4 # shape + move-result-object v4 # rect shape
+ +
+ invoke-static {v3, v6, v7, v4}, Landroidx/compose/foundation/BackgroundKt;->background-bw27NRU(Landroidx/compose/ui/Modifier;JLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier; # tint with scrim + invoke-static {v3, v6, v7, v4}, Landroidx/compose/foundation/BackgroundKt;->background-bw27NRU(Landroidx/compose/ui/Modifier;JLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier; # tint with scrim
+ +
+ move-result-object v3 # modifier + move-result-object v3 # tinted modifier
+ +
+ const/4 v4, 0x0 # $changed flags + const/4 v4, 0x0 # changed flags
+ +
+ invoke-static {v3, v10, v4}, Landroidx/compose/foundation/layout/SpacerKt;->Spacer(Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V # draw scrim + invoke-static {v3, v10, v4}, Landroidx/compose/foundation/layout/SpacerKt;->Spacer(Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V # draw scrim
+ +
+ :radiant_skip + :radiant_skip # skip target
+ invoke-interface {v10}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close group + invoke-interface {v10}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close group
+ +
.line 138 .line 138