mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-06-18 05:23:12 +10:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee8b25330d | |||
| 193d5ad90d | |||
| 942742e86e | |||
| 0b039110c9 | |||
| 045bbbab9a | |||
| 932f2a85dc | |||
| fc002615f4 | |||
| c89dd54fa6 | |||
| 53a4341d6b | |||
| c81d2edba3 | |||
| 9180129afc |
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,6 @@ import com.meowarex.rlmobile.ui.screens.about.AboutModel
|
||||
import com.meowarex.rlmobile.ui.screens.componentopts.ComponentOptionsModel
|
||||
import com.meowarex.rlmobile.ui.screens.home.HomeModel
|
||||
import com.meowarex.rlmobile.ui.screens.log.LogScreenModel
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogsListScreenModel
|
||||
import com.meowarex.rlmobile.ui.screens.patching.PatchingScreenModel
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionsModel
|
||||
import com.meowarex.rlmobile.ui.screens.permissions.PermissionsModel
|
||||
@@ -64,7 +63,6 @@ class ManagerApplication : Application() {
|
||||
factoryOf(::PatchOptionsModel)
|
||||
factoryOf(::ComponentOptionsModel)
|
||||
factoryOf(::LogScreenModel)
|
||||
factoryOf(::LogsListScreenModel)
|
||||
factoryOf(::PermissionsModel)
|
||||
viewModelOf(::UpdaterViewModel)
|
||||
})
|
||||
|
||||
@@ -12,8 +12,6 @@ import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
@@ -21,52 +19,19 @@ import kotlin.time.Duration
|
||||
import kotlin.time.Instant
|
||||
|
||||
/**
|
||||
* Central manager for storing all attempted installations and
|
||||
* their associated logs/crashes (not including manager crashes themselves).
|
||||
* In-memory store for the most recent install attempts
|
||||
*/
|
||||
class InstallLogManager(
|
||||
private val application: Application,
|
||||
private val prefs: PreferencesManager,
|
||||
private val json: Json,
|
||||
@Suppress("unused") private val json: Json,
|
||||
) {
|
||||
val logsDir = application.filesDir.resolve("install-logs").apply { mkdir() }
|
||||
private val entries = linkedMapOf<String, InstallLogData>()
|
||||
|
||||
/**
|
||||
* Lists all the install data entries that exist on disk, sorted decreasing by
|
||||
* the file creation date.
|
||||
* @return List of installation ids, most recent installation first.
|
||||
*/
|
||||
fun fetchInstallDataEntries(): List<String> {
|
||||
val files = logsDir.listFiles { it.extension == "json" } ?: emptyArray()
|
||||
|
||||
return files
|
||||
.sortedByDescending { it.lastModified() }
|
||||
.map { it.nameWithoutExtension }
|
||||
fun fetchInstallData(id: String): InstallLogData? = synchronized(entries) {
|
||||
entries[id]
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the install log from disk, if it exists.
|
||||
*/
|
||||
fun fetchInstallData(id: String): InstallLogData? {
|
||||
val path = logsDir.resolve("$id.json")
|
||||
if (!path.exists()) return null
|
||||
|
||||
return try {
|
||||
json.decodeFromStream(path.inputStream())
|
||||
} catch (t: Throwable) {
|
||||
Log.e(BuildConfig.TAG, "Failed to open install log $id", t)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteAllEntries() {
|
||||
logsDir.deleteRecursively()
|
||||
logsDir.mkdir()
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an install log entry to disk.
|
||||
*/
|
||||
suspend fun storeInstallData(
|
||||
id: String,
|
||||
installDate: Instant,
|
||||
@@ -75,8 +40,6 @@ class InstallLogManager(
|
||||
log: String,
|
||||
error: Throwable?,
|
||||
) {
|
||||
val path = logsDir.resolve("$id.json")
|
||||
|
||||
val data = InstallLogData(
|
||||
id = id,
|
||||
installDate = installDate,
|
||||
@@ -87,16 +50,16 @@ class InstallLogManager(
|
||||
errorStacktrace = error?.let { Log.getStackTraceString(it).trimEnd() },
|
||||
)
|
||||
|
||||
try {
|
||||
path.writeText(json.encodeToString(data))
|
||||
} catch (e: IOException) {
|
||||
Log.e(BuildConfig.TAG, "Failed to write log to disk", e)
|
||||
synchronized(entries) {
|
||||
entries[id] = data
|
||||
// Keep only the most recent few in memory.
|
||||
while (entries.size > MAX_ENTRIES) {
|
||||
val oldest = entries.keys.iterator().next()
|
||||
entries.remove(oldest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of details about the current installation environment.
|
||||
*/
|
||||
@Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants")
|
||||
@SuppressLint("UsableSpace")
|
||||
suspend fun getEnvironmentInfo(): String {
|
||||
@@ -139,6 +102,10 @@ class InstallLogManager(
|
||||
SOC: $soc
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val MAX_ENTRIES = 8
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
|
||||
+32
-3
@@ -92,18 +92,25 @@ class SmaliPatchStep(
|
||||
container.log("Recorded rl-locals bump: $smaliPath method≈\"$methodSubstring\" >= $newValue")
|
||||
}
|
||||
|
||||
val targetLine = lines.firstOrNull { it.startsWith("--- a/") }
|
||||
?: throw Error("Patch $patchFile is missing a '--- a/...' header")
|
||||
// Split into per-target sections — a single .patch may contain multiple
|
||||
// `--- a/...` blocks targeting different classes.
|
||||
val sections = splitMultiTargetPatch(lines)
|
||||
if (sections.isEmpty()) {
|
||||
throw Error("Patch $patchFile is missing a '--- a/...' header")
|
||||
}
|
||||
for (section in sections) {
|
||||
val targetLine = section.first { it.startsWith("--- a/") }
|
||||
val fullClassName = targetLine
|
||||
.removePrefix("--- a/")
|
||||
.removeSuffix(".smali")
|
||||
.trim()
|
||||
val patch = LoadedPatch(
|
||||
fullClassName = fullClassName,
|
||||
patch = UnifiedDiffUtils.parseUnifiedDiff(lines),
|
||||
patch = UnifiedDiffUtils.parseUnifiedDiff(section),
|
||||
)
|
||||
patches.add(patch)
|
||||
container.log("Loaded patch file $patchFile for class ${patch.fullClassName}")
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
throw Error("Failed to parse patch file $patchFile", t)
|
||||
}
|
||||
@@ -314,6 +321,28 @@ class SmaliPatchStep(
|
||||
private companion object {
|
||||
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(
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.previews.dialogs
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.meowarex.rlmobile.ui.screens.logs.components.dialogs.DeleteLogsDialog
|
||||
import com.meowarex.rlmobile.ui.theme.ManagerTheme
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun DeleteLogsDialogPreview() {
|
||||
ManagerTheme {
|
||||
DeleteLogsDialog(
|
||||
onConfirm = {},
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.previews.screens.logs
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogEntry
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogsScreenContent
|
||||
import com.meowarex.rlmobile.ui.theme.ManagerTheme
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.util.UUID
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun LogsListScreenNonePreview() {
|
||||
ManagerTheme {
|
||||
LogsScreenContent(
|
||||
logs = logs,
|
||||
onOpenLog = {},
|
||||
onDeleteLogs = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val logs: SnapshotStateList<LogEntry> = mutableStateListOf(
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = false,
|
||||
installDate = "5 min. ago, 10:18 AM",
|
||||
durationSecs = 18.555f,
|
||||
stacktracePreview = null,
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = true,
|
||||
installDate = "7 min. ago, 10:17 AM",
|
||||
durationSecs = 73.095f,
|
||||
stacktracePreview = persistentListOf(
|
||||
"kotlinx.coroutines.JobCancellationException: Job was cancelled; job=SupervisorJobImpl{Cancelling}@833e76f]",
|
||||
),
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = false,
|
||||
installDate = "Yesterday, 11:37 PM",
|
||||
durationSecs = 58.439f,
|
||||
stacktracePreview = null,
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = true,
|
||||
installDate = "Yesterday, 11:17 PM",
|
||||
durationSecs = 24.405f,
|
||||
stacktracePreview = persistentListOf(
|
||||
"java.lang.Error: Installation was aborted or cancelled",
|
||||
),
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = true,
|
||||
installDate = "Yesterday, 11:17 PM",
|
||||
durationSecs = 0.057f,
|
||||
stacktracePreview = persistentListOf(
|
||||
"java.lang.IllegalStateException: balls",
|
||||
"\tat com.meowarex.rlmobile.patcher.steps.prepare.FetchInfoStep.execute(FetchInfoStep.kt:31)",
|
||||
"\tat com.meowarex.rlmobile.patcher.steps.prepare.FetchInfoStep\$execute\\$1.invokeSuspend(Unknown Source:15)",
|
||||
),
|
||||
),
|
||||
LogEntry(
|
||||
id = UUID.randomUUID().toString(),
|
||||
isError = false,
|
||||
installDate = "Yesterday, 1:11 PM",
|
||||
durationSecs = 210.539f,
|
||||
stacktracePreview = null,
|
||||
),
|
||||
)
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.previews.screens.logs
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogsScreenContent
|
||||
import com.meowarex.rlmobile.ui.theme.ManagerTheme
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
private fun LogsListScreenNonePreview() {
|
||||
ManagerTheme {
|
||||
LogsScreenContent(
|
||||
logs = remember { mutableStateListOf() },
|
||||
onOpenLog = {},
|
||||
onDeleteLogs = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -117,18 +117,22 @@ class HomeModel(
|
||||
|
||||
val remote = remoteDataJson
|
||||
val currentPatches = remote?.patchesVersion?.toString()
|
||||
val installedPatches = installMetadata?.patchesVersion?.toString()
|
||||
val previousPatches = initialPrefPatchesVersion.ifEmpty {
|
||||
installMetadata?.patchesVersion?.toString().orEmpty()
|
||||
installedPatches.orEmpty()
|
||||
}
|
||||
val patchesFrom = previousPatches.takeIf { it.isNotEmpty() }
|
||||
val patchesTo = currentPatches ?: previousPatches.ifEmpty { "?" }
|
||||
val patchesUpdateAvailable = installedPatches != null
|
||||
&& currentPatches != null
|
||||
&& installedPatches != currentPatches
|
||||
add(
|
||||
VersionDelta(
|
||||
label = application.getString(R.string.manager_update_row_patches),
|
||||
iconRes = R.drawable.ic_extension,
|
||||
from = patchesFrom,
|
||||
to = patchesTo,
|
||||
tag = if (patchesFrom != null && patchesFrom != patchesTo)
|
||||
tag = if (patchesUpdateAvailable)
|
||||
application.getString(R.string.manager_update_tag_available) else null,
|
||||
)
|
||||
)
|
||||
@@ -142,13 +146,16 @@ class HomeModel(
|
||||
val tidalTo = currentTidal?.toString()
|
||||
?: previousTidal.takeIf { it > 0 }?.toString()
|
||||
?: "?"
|
||||
val tidalUpdateAvailable = installedTidalVersionCode > 0
|
||||
&& currentTidal != null
|
||||
&& installedTidalVersionCode != currentTidal
|
||||
add(
|
||||
VersionDelta(
|
||||
label = application.getString(R.string.manager_update_row_tidal),
|
||||
iconRes = R.drawable.ic_music_note,
|
||||
from = tidalFrom,
|
||||
to = tidalTo,
|
||||
tag = if (tidalFrom != null && tidalFrom != tidalTo)
|
||||
tag = if (tidalUpdateAvailable)
|
||||
application.getString(R.string.manager_update_tag_available) else null,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -32,13 +33,14 @@ import com.meowarex.rlmobile.ui.components.SegmentedButton
|
||||
import com.meowarex.rlmobile.ui.components.Tag
|
||||
import com.meowarex.rlmobile.ui.screens.about.AboutScreen
|
||||
import com.meowarex.rlmobile.ui.screens.home.components.CommitList
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogsListScreen
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionsScreen
|
||||
import com.meowarex.rlmobile.ui.screens.settings.SettingsScreen
|
||||
import com.meowarex.rlmobile.ui.widgets.managerupdate.ManagerUpdateDialog
|
||||
import com.meowarex.rlmobile.ui.widgets.updater.UpdaterViewModel
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
@Parcelize
|
||||
class HomeScreen : Screen, Parcelable {
|
||||
@@ -50,6 +52,9 @@ class HomeScreen : Screen, Parcelable {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val scope = rememberCoroutineScope()
|
||||
val model = koinScreenModel<HomeModel>()
|
||||
val activity = LocalContext.current as ComponentActivity
|
||||
val updater = koinViewModel<UpdaterViewModel>(viewModelStoreOwner = activity)
|
||||
val managerUpdateAvailable = updater.targetVersion != null
|
||||
|
||||
LifecycleResumeEffect(Unit) {
|
||||
model.refresh(delay = true)
|
||||
@@ -67,18 +72,20 @@ class HomeScreen : Screen, Parcelable {
|
||||
contentDescription = stringResource(R.string.navigation_refresh),
|
||||
)
|
||||
}
|
||||
if (managerUpdateAvailable) {
|
||||
IconButton(onClick = updater::reopenDialog) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_update),
|
||||
contentDescription = stringResource(R.string.action_update),
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { navigator.push(AboutScreen()) }) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_info),
|
||||
contentDescription = stringResource(R.string.navigation_about),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { navigator.push(LogsListScreen()) }) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_receipt),
|
||||
contentDescription = stringResource(R.string.navigation_logs),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { navigator.push(SettingsScreen()) }) {
|
||||
Icon(
|
||||
painterResource(R.drawable.ic_settings),
|
||||
@@ -107,6 +114,7 @@ class HomeScreen : Screen, Parcelable {
|
||||
is HomeState.Loaded -> HomeContent(
|
||||
state = state,
|
||||
commits = model.commits,
|
||||
managerUpdateAvailable = managerUpdateAvailable,
|
||||
onInstall = { navigator.pushOnce(PatchOptionsScreen()) },
|
||||
onRepatch = {
|
||||
scope.launchIO {
|
||||
@@ -134,6 +142,7 @@ class HomeScreen : Screen, Parcelable {
|
||||
private fun ColumnScope.HomeContent(
|
||||
state: HomeState.Loaded,
|
||||
commits: kotlinx.coroutines.flow.Flow<androidx.paging.PagingData<com.meowarex.rlmobile.network.models.GithubCommit>>,
|
||||
managerUpdateAvailable: Boolean,
|
||||
onInstall: () -> Unit,
|
||||
onRepatch: () -> Unit,
|
||||
onLaunch: () -> Unit,
|
||||
@@ -200,15 +209,19 @@ private fun ColumnScope.HomeContent(
|
||||
}
|
||||
}
|
||||
|
||||
val blockedByManagerUpdate = managerUpdateAvailable && (patchesBehind || tidalBehind)
|
||||
Button(
|
||||
onClick = if (install == null) onInstall else onRepatch,
|
||||
enabled = state.latestTidalVersionCode != null,
|
||||
enabled = state.latestTidalVersionCode != null && !blockedByManagerUpdate,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
val label = when {
|
||||
blockedByManagerUpdate -> "Manager Update Required"
|
||||
state.latestTidalVersionCode == null -> "Loading…"
|
||||
install == null -> "Install"
|
||||
install.isUpToDate == false -> "Update"
|
||||
patchesBehind && tidalBehind -> "Update Patches & TIDAL"
|
||||
patchesBehind -> "Update Patches"
|
||||
tidalBehind -> "Update TIDAL"
|
||||
else -> "Repatch"
|
||||
}
|
||||
Text(
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.core.screen.ScreenKey
|
||||
import cafe.adriel.voyager.koin.koinScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.meowarex.rlmobile.ui.screens.log.LogScreen
|
||||
import com.meowarex.rlmobile.ui.screens.logs.components.*
|
||||
import com.meowarex.rlmobile.ui.screens.logs.components.dialogs.DeleteLogsDialog
|
||||
import com.meowarex.rlmobile.ui.util.paddings.PaddingValuesSides
|
||||
import com.meowarex.rlmobile.ui.util.paddings.exclude
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class LogsListScreen : Screen, Parcelable {
|
||||
@IgnoredOnParcel
|
||||
override val key: ScreenKey
|
||||
get() = "LogsScreen"
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val model = koinScreenModel<LogsListScreenModel>()
|
||||
|
||||
var showWipeConfirmDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showWipeConfirmDialog) {
|
||||
DeleteLogsDialog(
|
||||
onConfirm = {
|
||||
showWipeConfirmDialog = false
|
||||
model.deleteLogs()
|
||||
},
|
||||
onDismiss = {
|
||||
showWipeConfirmDialog = false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
LogsScreenContent(
|
||||
logs = model.logEntries,
|
||||
onOpenLog = { navigator.push(LogScreen(installId = it)) },
|
||||
onDeleteLogs = { showWipeConfirmDialog = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LogsScreenContent(
|
||||
logs: SnapshotStateList<LogEntry>,
|
||||
onOpenLog: (id: String) -> Unit,
|
||||
onDeleteLogs: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
LogsListAppBar(
|
||||
onDeleteLogs = onDeleteLogs,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
contentPadding = paddingValues.exclude(PaddingValuesSides.Horizontal + PaddingValuesSides.Top),
|
||||
modifier = Modifier
|
||||
.padding(paddingValues.exclude(PaddingValuesSides.Bottom))
|
||||
.padding(vertical = 12.dp, horizontal = 22.dp)
|
||||
) {
|
||||
if (logs.isEmpty()) {
|
||||
item(key = "EMPTY") {
|
||||
LogsNone(
|
||||
modifier = Modifier.fillParentMaxSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(
|
||||
items = logs,
|
||||
contentType = { "LOG" },
|
||||
key = { it.id },
|
||||
) { data ->
|
||||
LogEntryCard(
|
||||
data = data,
|
||||
onClick = remember(onOpenLog, data.id) { { onOpenLog(data.id) } },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs
|
||||
|
||||
import android.app.Application
|
||||
import android.text.format.DateUtils
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.manager.InstallLogManager
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
class LogsListScreenModel(
|
||||
private val logsManager: InstallLogManager,
|
||||
private val application: Application,
|
||||
) : ScreenModel {
|
||||
/**
|
||||
* All the loaded log entries sorted descending by creation date.
|
||||
*/
|
||||
val logEntries = mutableStateListOf<LogEntry>()
|
||||
|
||||
init {
|
||||
loadLogsList()
|
||||
}
|
||||
|
||||
fun deleteLogs() = screenModelScope.launchIO {
|
||||
logsManager.deleteAllEntries()
|
||||
|
||||
mainThread {
|
||||
logEntries.clear()
|
||||
application.showToast(R.string.logs_status_delete_success)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadLogsList() = screenModelScope.launchIO {
|
||||
for (installId in logsManager.fetchInstallDataEntries()) {
|
||||
val data = logsManager.fetchInstallData(id = installId)
|
||||
?: continue
|
||||
|
||||
val entry = LogEntry(
|
||||
id = data.id,
|
||||
isError = data.isError,
|
||||
installDate = DateUtils.getRelativeDateTimeString(
|
||||
/* c = */ application,
|
||||
/* time = */ data.installDate.toEpochMilliseconds(),
|
||||
/* minResolution = */ DateUtils.SECOND_IN_MILLIS,
|
||||
/* transitionResolution = */ DateUtils.WEEK_IN_MILLIS,
|
||||
/* flags = */ DateUtils.FORMAT_ABBREV_ALL,
|
||||
).toString(),
|
||||
durationSecs = data.installDuration.inWholeMilliseconds / 1000f,
|
||||
stacktracePreview = data.errorStacktrace
|
||||
?.splitToSequence('\n')
|
||||
?.take(3)
|
||||
?.toImmutableList(),
|
||||
)
|
||||
|
||||
mainThread { logEntries += entry }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class LogEntry(
|
||||
val id: String,
|
||||
val isError: Boolean,
|
||||
val installDate: String,
|
||||
val durationSecs: Float,
|
||||
val stacktracePreview: ImmutableList<String>?,
|
||||
)
|
||||
-122
@@ -1,122 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.*
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.patcher.steps.base.StepState
|
||||
import com.meowarex.rlmobile.ui.screens.logs.LogEntry
|
||||
import com.meowarex.rlmobile.ui.screens.patching.components.StepStateIcon
|
||||
import com.meowarex.rlmobile.ui.screens.patching.components.TimeElapsed
|
||||
|
||||
@Composable
|
||||
fun LogEntryCard(
|
||||
data: LogEntry,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val errorColor = MaterialTheme.colorScheme.error
|
||||
|
||||
ElevatedCard(
|
||||
shape = RectangleShape,
|
||||
modifier = modifier
|
||||
.clickable(onClick = onClick)
|
||||
.clip(RoundedCornerShape(topStart = 6.dp, 12.0.dp, bottomStart = 6.dp, bottomEnd = 12.0.dp))
|
||||
.drawWithCache {
|
||||
val color = when (data.isError) {
|
||||
true -> errorColor
|
||||
false -> Color(0xFF59B463)
|
||||
}
|
||||
|
||||
onDrawWithContent {
|
||||
drawContent()
|
||||
drawRect(
|
||||
color = color,
|
||||
alpha = .8f,
|
||||
topLeft = Offset.Zero,
|
||||
size = Size(4.dp.toPx(), size.height),
|
||||
)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 18.dp, horizontal = 22.dp),
|
||||
) {
|
||||
StepStateIcon(
|
||||
state = if (data.isError) StepState.Error else StepState.Success,
|
||||
size = 24.dp,
|
||||
)
|
||||
|
||||
Column {
|
||||
Text(
|
||||
text = when (data.isError) {
|
||||
true -> stringResource(R.string.status_failed)
|
||||
false -> stringResource(R.string.status_success)
|
||||
},
|
||||
)
|
||||
|
||||
Text(
|
||||
text = data.installDate,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
modifier = Modifier.alpha(.6f),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.weight(1f, fill = true))
|
||||
|
||||
TimeElapsed(
|
||||
seconds = data.durationSecs,
|
||||
modifier = Modifier.alpha(.9f),
|
||||
)
|
||||
}
|
||||
|
||||
if (data.stacktracePreview != null) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 26.dp, end = 20.dp, bottom = 18.dp)
|
||||
// https://stackoverflow.com/a/76270310/13964629
|
||||
.graphicsLayer(
|
||||
alpha = .95f,
|
||||
compositingStrategy = CompositingStrategy.Offscreen,
|
||||
)
|
||||
.drawWithContent {
|
||||
val colors = listOf(Color.Black, Color.Black, Color.Transparent)
|
||||
drawContent()
|
||||
drawRect(
|
||||
brush = Brush.verticalGradient(colors),
|
||||
blendMode = BlendMode.DstIn,
|
||||
)
|
||||
}
|
||||
) {
|
||||
// The stacktrace is separated into multiple Text elements because ellipsis is not supported per-line
|
||||
for (line in data.stacktracePreview) key(line) {
|
||||
Text(
|
||||
text = line,
|
||||
softWrap = false,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
lineHeight = 18.sp,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
fontFamily = FontFamily.Companion.Monospace,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs.components
|
||||
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.components.BackButton
|
||||
import com.meowarex.rlmobile.ui.screens.settings.SettingsScreen
|
||||
|
||||
@Composable
|
||||
fun LogsListAppBar(
|
||||
onDeleteLogs: () -> Unit,
|
||||
) {
|
||||
TopAppBar(
|
||||
navigationIcon = { BackButton() },
|
||||
title = { Text(stringResource(R.string.logs_title)) },
|
||||
actions = {
|
||||
val navigator = LocalNavigator.current
|
||||
|
||||
IconButton(onClick = onDeleteLogs) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_delete_forever),
|
||||
contentDescription = stringResource(R.string.logs_action_delete_all)
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { navigator?.push(SettingsScreen()) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_settings),
|
||||
contentDescription = stringResource(R.string.navigation_settings)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
|
||||
@Composable
|
||||
fun LogsNone(modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier) {
|
||||
Column(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_reciept_off),
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.logs_none),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.alpha(.8f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package com.meowarex.rlmobile.ui.screens.logs.components.dialogs
|
||||
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
|
||||
@Composable
|
||||
fun DeleteLogsDialog(
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
icon = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_delete_forever),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(32.dp),
|
||||
)
|
||||
},
|
||||
title = { Text(stringResource(R.string.logs_wipe_title)) },
|
||||
text = {
|
||||
Text(
|
||||
text = stringResource(R.string.logs_wipe_desc),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = onConfirm,
|
||||
) {
|
||||
Text(stringResource(R.string.action_confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(
|
||||
onClick = onDismiss,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
) {
|
||||
Text(stringResource(R.string.action_cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
+52
-4
@@ -4,19 +4,31 @@ import androidx.annotation.StringRes
|
||||
import com.meowarex.rlmobile.R
|
||||
|
||||
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>,
|
||||
@StringRes val titleRes: Int,
|
||||
@StringRes val descRes: Int,
|
||||
val requires: List<KnownPatch> = emptyList(),
|
||||
val disables: List<KnownPatch> = emptyList(),
|
||||
) {
|
||||
// Dependency-first order (later refs need backward resolution)
|
||||
// Dependency-first order (later refs need backward resolution).
|
||||
// The `order` field controls display order; declaration order doesn't matter.
|
||||
LyricsDisableCover(
|
||||
order = 41,
|
||||
fileNames = listOf("lyrics-disable-cover.patch"),
|
||||
titleRes = R.string.patch_lyrics_disable_cover_title,
|
||||
descRes = R.string.patch_lyrics_disable_cover_desc,
|
||||
),
|
||||
LyricsReplaceLyricsButton(
|
||||
order = 42,
|
||||
fileNames = listOf(
|
||||
"lyrics-replace-lyrics-button.patch",
|
||||
"lyrics-sparkle-conditional-visibility.patch",
|
||||
@@ -25,30 +37,60 @@ enum class KnownPatch(
|
||||
descRes = R.string.patch_lyrics_replace_button_desc,
|
||||
),
|
||||
LyricsReplaceShareButton(
|
||||
order = 43,
|
||||
fileNames = listOf("lyrics-replace-share-button.patch"),
|
||||
titleRes = R.string.patch_lyrics_replace_share_button_title,
|
||||
descRes = R.string.patch_lyrics_replace_share_button_desc,
|
||||
),
|
||||
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(
|
||||
order = 30,
|
||||
fileNames = listOf("player-backdrop.patch"),
|
||||
titleRes = R.string.patch_player_backdrop_title,
|
||||
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(
|
||||
order = 100,
|
||||
fileNames = listOf("debug-menu-unlock.patch"),
|
||||
titleRes = R.string.patch_debug_menu_unlock_title,
|
||||
descRes = R.string.patch_debug_menu_unlock_desc,
|
||||
),
|
||||
LyricsProgressPill(
|
||||
order = 40,
|
||||
fileNames = listOf(
|
||||
"lyrics-progress-pill.patch",
|
||||
"lyrics-fade-region.patch",
|
||||
),
|
||||
titleRes = R.string.patch_lyrics_progress_pill_title,
|
||||
descRes = R.string.patch_lyrics_progress_pill_desc,
|
||||
requires = listOf(LyricsDisableCover, LyricsReplaceShareButton),
|
||||
requires = listOf(LyricsDisableCover, LyricsReplaceLyricsButton, LyricsReplaceShareButton),
|
||||
),
|
||||
EnableLegacyUi(
|
||||
order = 10,
|
||||
fileNames = listOf("enable-legacy-ui.patch"),
|
||||
titleRes = R.string.patch_enable_legacy_ui_title,
|
||||
descRes = R.string.patch_enable_legacy_ui_desc,
|
||||
@@ -57,15 +99,21 @@ enum class KnownPatch(
|
||||
LyricsDisableCover,
|
||||
LyricsReplaceLyricsButton,
|
||||
LyricsReplaceShareButton,
|
||||
LyricsRlApi,
|
||||
LyricsKeepControlsVisible,
|
||||
PlayerBackdrop,
|
||||
LyricsProgressPill,
|
||||
CoverEverywhere,
|
||||
),
|
||||
);
|
||||
|
||||
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(
|
||||
compareBy({ it == DebugMenuUnlock }, { it.fileNames.first() })
|
||||
compareBy({ it.order }, { it.fileNames.first() })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -55,6 +55,10 @@ class UpdaterViewModel(
|
||||
showDialog = false
|
||||
}
|
||||
|
||||
fun reopenDialog() {
|
||||
if (targetVersion != null) showDialog = true
|
||||
}
|
||||
|
||||
fun triggerUpdate() = viewModelScope.launchIO {
|
||||
if (!isWorking.compareAndSet(expect = false, update = true))
|
||||
return@launchIO
|
||||
|
||||
@@ -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">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M480,840q-75,0 -140.5,-28.5t-114,-77q-48.5,-48.5 -77,-114T120,480q0,-75 28.5,-140.5t77,-114q48.5,-48.5 114,-77T480,120q82,0 155.5,35T760,254v-54q0,-17 11.5,-28.5T800,160q17,0 28.5,11.5T840,200v160q0,17 -11.5,28.5T800,400L640,400q-17,0 -28.5,-11.5T600,360q0,-17 11.5,-28.5T640,320h70q-41,-56 -101,-88t-129,-32q-117,0 -198.5,81.5T200,480q0,117 81.5,198.5T480,760q95,0 170,-57t99,-147q5,-16 18,-24t29,-6q17,2 27,14.5t6,27.5q-29,119 -126,195.5T480,840ZM520,464 L620,564q11,11 11,28t-11,28q-11,11 -28,11t-28,-11L452,508q-6,-6 -9,-13.5t-3,-15.5v-159q0,-17 11.5,-28.5T480,280q17,0 28.5,11.5T520,320v144Z" />
|
||||
android:pathData="M480,640q17,0 28.5,-11.5T520,600v-184l64,64q11,11 28,11t28,-11q11,-11 11,-28t-11,-28L508,308q-12,-12 -28,-12t-28,12L324,424q-11,11 -11,28t11,28q11,11 28,11t28,-11l64,-64v184q0,17 11.5,28.5T480,640Zm0,240q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880Z" />
|
||||
</vector>
|
||||
|
||||
@@ -129,7 +129,6 @@
|
||||
|
||||
<string name="navigation_back">Back</string>
|
||||
<string name="navigation_about">About</string>
|
||||
<string name="navigation_logs">Logs</string>
|
||||
<string name="navigation_settings">Settings</string>
|
||||
<string name="navigation_refresh">Refresh</string>
|
||||
|
||||
@@ -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_replace_button_title">Replace Lyrics Button</string>
|
||||
<string name="patch_lyrics_replace_button_desc">Replaces the Lyrics button with the RL Sparkle!</string>
|
||||
<string name="patch_lyrics_rl_api_title">Radiant Lyrics API</string>
|
||||
<string name="patch_lyrics_rl_api_desc">Use Radiant Lyrics API to fetch Lyrics (Higher quality & More providers)</string>
|
||||
<string name="patch_sticky_lyrics_title">Sticky Lyrics</string>
|
||||
<string name="patch_sticky_lyrics_desc">Always Forces the Lyrics page to be opened (aslong as the track has lyrics)</string>
|
||||
<string name="patch_lyrics_keep_controls_title">Keep Controls Visible</string>
|
||||
<string
|
||||
name="patch_lyrics_keep_controls_desc"
|
||||
>Inverts the auto-hide behavior on the lyrics screen, playback controls stay visible by default and only hide when you tap them off.</string>
|
||||
<string name="patch_player_backdrop_title">Player Backdrop</string>
|
||||
<string name="patch_player_backdrop_desc">Restores the legacy translucent backdrop blur behind the player.</string>
|
||||
<string name="patch_cover_everywhere_title">Cover Everywhere - WIP</string>
|
||||
<string name="patch_cover_everywhere_desc">Applies the blurred backdrop to every Compose based page (Home & Collection)</string>
|
||||
<string name="patch_lyrics_replace_share_button_title">Replace Share Button</string>
|
||||
<string
|
||||
name="patch_lyrics_replace_share_button_desc"
|
||||
@@ -293,13 +302,6 @@
|
||||
<string name="log_action_export_apk">Export APK</string>
|
||||
<string name="log_action_share">Share log</string>
|
||||
|
||||
<string name="logs_title">Logs</string>
|
||||
<string name="logs_none">No logs present!</string>
|
||||
<string name="logs_action_delete_all">Delete all logs</string>
|
||||
<string name="logs_status_delete_success">Successfully deleted all logs</string>
|
||||
<string name="logs_wipe_title">Clear Logs</string>
|
||||
<string name="logs_wipe_desc">Are you sure you want to permanently delete all the logs?</string>
|
||||
|
||||
<string name="notif_group_install_title">Active installation</string>
|
||||
<string name="notif_group_install_desc">Progress notifications sent during a minimized installation</string>
|
||||
<string name="notif_install_ready_title">Radiant Lyrics installation ready!</string>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/com/tidal/wave2/foundation/WaveScaffoldKt.smali
|
||||
+++ b/com/tidal/wave2/foundation/WaveScaffoldKt.smali
|
||||
@@ -889,6 +889,10 @@
|
||||
.line 386
|
||||
.line 387
|
||||
:cond_20
|
||||
+ const/4 v0, 0x0 # render $changed flag
|
||||
+
|
||||
+ invoke-static {v1, v0}, Lradiant/HomeBackdrop;->render(Landroidx/compose/runtime/Composer;I)V # render album-art backdrop
|
||||
+
|
||||
new-instance v0, Lcom/tidal/wave2/foundation/WaveScaffoldKt$WaveScaffold$2;
|
||||
|
||||
.line 388
|
||||
@@ -991,7 +995,7 @@
|
||||
|
||||
.line 436
|
||||
.line 437
|
||||
- move-wide v15, v10
|
||||
+ const-wide/16 v15, 0x0 # transparent containerColor
|
||||
|
||||
.line 438
|
||||
const/4 v11, 0x0
|
||||
@@ -0,0 +1,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
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"tidalVersionCode": 9090,
|
||||
"tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk",
|
||||
"patchesVersion": "0.7.6"
|
||||
"patchesVersion": "0.9.1"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,26 @@
|
||||
.class public final Lradiant/StickyLyrics;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static volatile enabled:Z
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-boolean v0, Lradiant/StickyLyrics;->enabled:Z
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,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;
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user