mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-06-18 05:23:12 +10:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8501ccb0a9 | |||
| ad6ebac6ce | |||
| 320148e774 | |||
| d8e119eb4b | |||
| 10e5dcf365 | |||
| 1c65c5669a | |||
| 961291166c | |||
| 25852aaea1 | |||
| c6b18d1aa9 | |||
| ee8b25330d | |||
| 193d5ad90d | |||
| 942742e86e | |||
| 0b039110c9 | |||
| 045bbbab9a | |||
| 932f2a85dc | |||
| fc002615f4 | |||
| c89dd54fa6 | |||
| 53a4341d6b | |||
| c81d2edba3 | |||
| 9180129afc | |||
| d9de3207f5 | |||
| 59fe232ae4 | |||
| e105a3eb35 | |||
| e7b69a8deb | |||
| 324a6eb6c8 | |||
| c5962ad1a8 | |||
| 77ab041b97 |
@@ -48,6 +48,8 @@ jobs:
|
||||
run: |
|
||||
mv ./dist/app-release.apk ./dist/rl-manager.apk
|
||||
cp patches/data.json ./dist/data.json
|
||||
# Point each release's data.json at its own assets so historical releases stay self-contained.
|
||||
sed -i "s|releases/download/latest/|releases/download/v${{ needs.Version.outputs.version }}/|g" ./dist/data.json
|
||||
cd patches && zip -r ../dist/patches.zip . -x "data.json" && cd ..
|
||||
|
||||
tidal_src=$(find tidal-apk -maxdepth 1 \( -name "*.apk" -o -name "*.apkm" \) | head -1)
|
||||
@@ -58,7 +60,7 @@ jobs:
|
||||
if [[ "$tidal_src" == *.apkm ]]; then
|
||||
echo "Merging splits from $tidal_src via APKEditor"
|
||||
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
|
||||
echo "Merged tidal-stock.apk:"
|
||||
ls -la ./dist/tidal-stock.apk
|
||||
@@ -67,10 +69,10 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Publish release
|
||||
uses: marvinpinto/action-automatic-releases@latest
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
automatic_release_tag: latest
|
||||
tag_name: v${{ needs.Version.outputs.version }}
|
||||
name: v${{ needs.Version.outputs.version }}
|
||||
prerelease: false
|
||||
title: v${{ needs.Version.outputs.version }}
|
||||
make_latest: "true"
|
||||
files: ./dist/**
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -142,7 +142,7 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
val targetDir = when (componentType) {
|
||||
"injector" -> paths.customInjectorsDir
|
||||
"tidal" -> paths.customTidalApksDir
|
||||
"patches" -> paths.customPatchesDir
|
||||
else -> {
|
||||
Log.w(BuildConfig.TAG, "Extra $EXTRA_COMPONENT_TYPE is not a valid value!")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,7 +26,7 @@ class PathManager(
|
||||
|
||||
val customComponentsDir = patchingDir.resolve("custom")
|
||||
|
||||
val customInjectorsDir = customComponentsDir.resolve("injector")
|
||||
val customTidalApksDir = customComponentsDir.resolve("tidal")
|
||||
|
||||
val customPatchesDir = customComponentsDir.resolve("patches")
|
||||
|
||||
@@ -54,7 +54,7 @@ class PathManager(
|
||||
.resolve("patches")
|
||||
.resolve("$version.zip")
|
||||
|
||||
fun customInjectors() = customInjectorsDir.listFiles()?.asList() ?: emptyList()
|
||||
fun customTidalApks() = customTidalApksDir.listFiles()?.asList() ?: emptyList()
|
||||
|
||||
fun customSmaliPatches() = customPatchesDir.listFiles()?.asList() ?: emptyList()
|
||||
}
|
||||
|
||||
@@ -12,7 +12,10 @@ class PreferencesManager(preferences: SharedPreferences) : BasePreferenceManager
|
||||
var devMode by booleanPreference("dev_mode", false)
|
||||
var installer by enumPreference<InstallerSetting>("installer", InstallerSetting.PackageInstaller)
|
||||
var keepPatchedApks by booleanPreference("keep_patched_apks", false)
|
||||
var showNetworkWarning by booleanPreference("show_network_warning", true)
|
||||
var showPlayProtectWarning by booleanPreference("show_play_protect_warning", true)
|
||||
var autoUpdateCheck by booleanPreference("auto_update_check", true)
|
||||
|
||||
var lastSeenManagerVersion by stringPreference("last_seen_manager_version", "")
|
||||
var lastSeenPatchesVersion by stringPreference("last_seen_patches_version", "")
|
||||
var lastSeenTidalVersionCode by intPreference("last_seen_tidal_version_code", -1)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class TidalPatchRunner(
|
||||
RestoreDownloadsStep(),
|
||||
|
||||
// Download
|
||||
DownloadTidalStep(),
|
||||
DownloadTidalStep(options.customTidalApk),
|
||||
DownloadPatchesStep(options.customPatches),
|
||||
CopyDependenciesStep(),
|
||||
|
||||
|
||||
+25
-2
@@ -5,12 +5,17 @@ import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.manager.PathManager
|
||||
import com.meowarex.rlmobile.patcher.StepRunner
|
||||
import com.meowarex.rlmobile.patcher.steps.base.DownloadStep
|
||||
import com.meowarex.rlmobile.patcher.steps.base.StepState
|
||||
import com.meowarex.rlmobile.patcher.steps.prepare.FetchInfoStep
|
||||
import com.meowarex.rlmobile.ui.screens.componentopts.PatchComponent
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
@Stable
|
||||
class DownloadTidalStep : DownloadStep<Int>(), KoinComponent {
|
||||
class DownloadTidalStep(
|
||||
private val custom: PatchComponent?,
|
||||
) : DownloadStep<Int>(), KoinComponent {
|
||||
private val paths: PathManager by inject()
|
||||
|
||||
override val localizedName = R.string.patch_step_dl_tidal_apk
|
||||
@@ -22,5 +27,23 @@ class DownloadTidalStep : DownloadStep<Int>(), KoinComponent {
|
||||
container.getStep<FetchInfoStep>().data.tidalApkUrl
|
||||
|
||||
override fun getStoredFile(container: StepRunner) =
|
||||
paths.cachedTidalApk(getVersion(container))
|
||||
custom?.getFile(paths) ?: paths.cachedTidalApk(getVersion(container))
|
||||
|
||||
override suspend fun execute(container: StepRunner) {
|
||||
if (custom != null) {
|
||||
container.log("Using custom TIDAL APK with version ${custom.version} imported ${custom.timestamp}")
|
||||
|
||||
if (!custom.getFile(paths).exists()) {
|
||||
throw FileNotFoundException(
|
||||
"Selected custom TIDAL APK does not exist on disk! If this is an update, " +
|
||||
"updates cannot occur when the originally selected custom component has been deleted."
|
||||
)
|
||||
}
|
||||
|
||||
state = StepState.Skipped
|
||||
return
|
||||
}
|
||||
|
||||
super.execute(container)
|
||||
}
|
||||
}
|
||||
|
||||
+41
-12
@@ -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")
|
||||
val fullClassName = targetLine
|
||||
.removePrefix("--- a/")
|
||||
.removeSuffix(".smali")
|
||||
.trim()
|
||||
val patch = LoadedPatch(
|
||||
fullClassName = fullClassName,
|
||||
patch = UnifiedDiffUtils.parseUnifiedDiff(lines),
|
||||
)
|
||||
patches.add(patch)
|
||||
container.log("Loaded patch file $patchFile for class ${patch.fullClassName}")
|
||||
// Split into per-target sections — a single .patch may contain multiple
|
||||
// `--- a/...` blocks targeting different classes.
|
||||
val sections = splitMultiTargetPatch(lines)
|
||||
if (sections.isEmpty()) {
|
||||
throw Error("Patch $patchFile is missing a '--- a/...' header")
|
||||
}
|
||||
for (section in sections) {
|
||||
val targetLine = section.first { it.startsWith("--- a/") }
|
||||
val fullClassName = targetLine
|
||||
.removePrefix("--- a/")
|
||||
.removeSuffix(".smali")
|
||||
.trim()
|
||||
val patch = LoadedPatch(
|
||||
fullClassName = fullClassName,
|
||||
patch = UnifiedDiffUtils.parseUnifiedDiff(section),
|
||||
)
|
||||
patches.add(patch)
|
||||
container.log("Loaded patch file $patchFile for class ${patch.fullClassName}")
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
throw Error("Failed to parse patch file $patchFile", t)
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.meowarex.rlmobile.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun Tag(text: String) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
shape = RoundedCornerShape(6.dp),
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
-96
@@ -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,
|
||||
)
|
||||
}
|
||||
-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 = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
-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.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 = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
+12
-6
@@ -30,6 +30,12 @@ private fun ComponentOptionsScreenPreview(
|
||||
selected = parameters.selected,
|
||||
onSelectComponent = {},
|
||||
onDeleteComponent = {},
|
||||
onImportFromUri = {},
|
||||
releasesExpanded = false,
|
||||
releasesState = com.meowarex.rlmobile.ui.screens.componentopts.ComponentOptionsModel.ReleasesState.Idle,
|
||||
onToggleReleases = {},
|
||||
onImportRelease = {},
|
||||
importingReleaseTag = null,
|
||||
onBackPressed = {},
|
||||
)
|
||||
}
|
||||
@@ -44,27 +50,27 @@ private data class ComponentOptionsParameters(
|
||||
private class ComponentOptionsParametersProvider : PreviewParameterProvider<ComponentOptionsParameters> {
|
||||
private val components = persistentListOf(
|
||||
PatchComponent(
|
||||
type = PatchComponent.Type.Injector,
|
||||
type = PatchComponent.Type.TidalApk,
|
||||
version = SemVer(1, 2, 3),
|
||||
timestamp = Clock.System.now(),
|
||||
),
|
||||
PatchComponent(
|
||||
type = PatchComponent.Type.Injector,
|
||||
type = PatchComponent.Type.TidalApk,
|
||||
version = SemVer(2, 3, 1),
|
||||
timestamp = Clock.System.now() - 10.minutes,
|
||||
),
|
||||
PatchComponent(
|
||||
type = PatchComponent.Type.Injector,
|
||||
type = PatchComponent.Type.TidalApk,
|
||||
version = SemVer(2, 3, 1),
|
||||
timestamp = Clock.System.now() - 1.days,
|
||||
),
|
||||
PatchComponent(
|
||||
type = PatchComponent.Type.Injector,
|
||||
type = PatchComponent.Type.TidalApk,
|
||||
version = SemVer(0, 0, 1),
|
||||
timestamp = Clock.System.now() - 10.hours,
|
||||
),
|
||||
PatchComponent(
|
||||
type = PatchComponent.Type.Injector,
|
||||
type = PatchComponent.Type.TidalApk,
|
||||
version = SemVer(3, 0, 2),
|
||||
timestamp = Clock.System.now() - 7.days,
|
||||
),
|
||||
@@ -72,7 +78,7 @@ private class ComponentOptionsParametersProvider : PreviewParameterProvider<Comp
|
||||
|
||||
override val values = sequenceOf(
|
||||
ComponentOptionsParameters(
|
||||
componentType = PatchComponent.Type.Injector,
|
||||
componentType = PatchComponent.Type.TidalApk,
|
||||
components = components,
|
||||
selected = null,
|
||||
),
|
||||
|
||||
+10
-7
@@ -30,13 +30,16 @@ private fun PatchOptionsScreenPreview(
|
||||
packageName = parameters.packageName,
|
||||
packageNameState = parameters.packageNameState,
|
||||
setPackageName = {},
|
||||
customInjector = parameters.customInjector,
|
||||
onSelectCustomInjector = {},
|
||||
customTidalApk = parameters.customTidalApk,
|
||||
onSelectCustomTidalApk = {},
|
||||
customPatches = parameters.customPatches,
|
||||
onSelectCustomPatches = {},
|
||||
enabledPatchCount = KnownPatch.All.size,
|
||||
isPatchEnabled = { true },
|
||||
onTogglePatch = { _, _ -> },
|
||||
patchLockState = { PatchLock.Free },
|
||||
variantIndex = { 0 },
|
||||
onSelectVariant = { _, _ -> },
|
||||
isConfigValid = parameters.isConfigValid,
|
||||
onInstall = {},
|
||||
)
|
||||
@@ -51,7 +54,7 @@ private data class PatchOptionsParameters(
|
||||
val appNameIsError: Boolean,
|
||||
val packageName: String,
|
||||
val packageNameState: PackageNameState,
|
||||
val customInjector: PatchComponent?,
|
||||
val customTidalApk: PatchComponent?,
|
||||
val customPatches: PatchComponent?,
|
||||
val isConfigValid: Boolean,
|
||||
)
|
||||
@@ -66,7 +69,7 @@ private class PatchOptionsParametersProvider : PreviewParameterProvider<PatchOpt
|
||||
appNameIsError = false,
|
||||
packageName = PatchOptions.Default.packageName,
|
||||
packageNameState = PackageNameState.Ok,
|
||||
customInjector = null,
|
||||
customTidalApk = null,
|
||||
customPatches = null,
|
||||
isConfigValid = true,
|
||||
),
|
||||
@@ -78,7 +81,7 @@ private class PatchOptionsParametersProvider : PreviewParameterProvider<PatchOpt
|
||||
appNameIsError = true,
|
||||
packageName = "a b",
|
||||
packageNameState = PackageNameState.Invalid,
|
||||
customInjector = null,
|
||||
customTidalApk = null,
|
||||
customPatches = null,
|
||||
isConfigValid = false,
|
||||
),
|
||||
@@ -90,8 +93,8 @@ private class PatchOptionsParametersProvider : PreviewParameterProvider<PatchOpt
|
||||
appNameIsError = false,
|
||||
packageName = PatchOptions.Default.packageName,
|
||||
packageNameState = PackageNameState.Taken,
|
||||
customInjector = PatchComponent(
|
||||
type = PatchComponent.Type.Injector,
|
||||
customTidalApk = PatchComponent(
|
||||
type = PatchComponent.Type.TidalApk,
|
||||
version = SemVer(1, 2, 3),
|
||||
timestamp = Clock.System.now(),
|
||||
),
|
||||
|
||||
-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 = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
+169
-1
@@ -1,26 +1,44 @@
|
||||
package com.meowarex.rlmobile.ui.screens.componentopts
|
||||
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.*
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.meowarex.rlmobile.BuildConfig
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.manager.PathManager
|
||||
import com.meowarex.rlmobile.manager.download.KtorDownloadManager
|
||||
import com.meowarex.rlmobile.network.models.GithubRelease
|
||||
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
|
||||
import com.meowarex.rlmobile.network.utils.SemVer
|
||||
import com.meowarex.rlmobile.network.utils.fold
|
||||
import com.meowarex.rlmobile.ui.util.ScreenModelWithResult
|
||||
import com.meowarex.rlmobile.ui.util.ScreenResultKey
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import kotlin.time.Instant
|
||||
|
||||
class ComponentOptionsModel(
|
||||
screenResultKey: ScreenResultKey,
|
||||
private val paths: PathManager,
|
||||
private val context: Application,
|
||||
private val github: RadiantLyricsGithubService,
|
||||
private val downloader: KtorDownloadManager,
|
||||
) : ScreenModelWithResult<PatchComponent?>(screenResultKey) {
|
||||
val components = mutableStateListOf<PatchComponent>()
|
||||
var selected by mutableStateOf<PatchComponent?>(null)
|
||||
private set
|
||||
|
||||
var releasesExpanded by mutableStateOf(false)
|
||||
private set
|
||||
var releasesState by mutableStateOf<ReleasesState>(ReleasesState.Idle)
|
||||
private set
|
||||
var importingReleaseTag by mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
fun selectComponent(component: PatchComponent?) {
|
||||
selected = component
|
||||
}
|
||||
@@ -39,7 +57,7 @@ class ComponentOptionsModel(
|
||||
*/
|
||||
suspend fun refreshComponents(type: PatchComponent.Type) {
|
||||
val files = when (type) {
|
||||
PatchComponent.Type.Injector -> paths.customInjectors()
|
||||
PatchComponent.Type.TidalApk -> paths.customTidalApks()
|
||||
PatchComponent.Type.Patches -> paths.customSmaliPatches()
|
||||
}
|
||||
|
||||
@@ -64,7 +82,157 @@ class ComponentOptionsModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun importFromUri(uri: Uri, type: PatchComponent.Type) = screenModelScope.launchIO {
|
||||
try {
|
||||
val targetDir = when (type) {
|
||||
PatchComponent.Type.TidalApk -> paths.customTidalApksDir
|
||||
PatchComponent.Type.Patches -> paths.customPatchesDir
|
||||
}
|
||||
val ext = when (type) {
|
||||
PatchComponent.Type.TidalApk -> "apk"
|
||||
PatchComponent.Type.Patches -> "zip"
|
||||
}
|
||||
targetDir.mkdirs()
|
||||
|
||||
val tempFile = targetDir.resolve("import-${System.currentTimeMillis()}.tmp")
|
||||
context.contentResolver.openInputStream(uri)?.use { input ->
|
||||
tempFile.outputStream().use { output -> input.copyTo(output) }
|
||||
} ?: throw IllegalStateException("Could not open input stream for $uri")
|
||||
|
||||
val sourceDisplayName = queryDisplayName(uri)
|
||||
val version = when (type) {
|
||||
PatchComponent.Type.TidalApk -> readApkVersion(tempFile)
|
||||
?: extractVersionFromName(sourceDisplayName)
|
||||
?: FALLBACK_VERSION
|
||||
PatchComponent.Type.Patches -> extractVersionFromName(sourceDisplayName)
|
||||
?: FALLBACK_VERSION
|
||||
}
|
||||
|
||||
val finalName = "${System.currentTimeMillis()}_$version.$ext"
|
||||
val finalFile = targetDir.resolve(finalName)
|
||||
if (!tempFile.renameTo(finalFile)) {
|
||||
tempFile.copyTo(finalFile, overwrite = true)
|
||||
tempFile.delete()
|
||||
}
|
||||
|
||||
refreshComponents(type)
|
||||
mainThread { context.showToast(R.string.intent_import_component_success, finalName) }
|
||||
} catch (t: Throwable) {
|
||||
Log.e(BuildConfig.TAG, "Failed to import custom component from $uri", t)
|
||||
mainThread { context.showToast(R.string.intent_import_component_failure) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun queryDisplayName(uri: Uri): String? = try {
|
||||
context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)
|
||||
?.use { cursor ->
|
||||
if (cursor.moveToFirst()) cursor.getString(0) else null
|
||||
}
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun readApkVersion(apkFile: File): String? = try {
|
||||
@Suppress("DEPRECATION")
|
||||
context.packageManager.getPackageArchiveInfo(apkFile.absolutePath, 0)
|
||||
?.versionName
|
||||
?.let { SemVer.parseOrNull(it) }
|
||||
?.toString()
|
||||
} catch (_: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun extractVersionFromName(name: String?): String? {
|
||||
if (name == null) return null
|
||||
return """(\d+\.\d+\.\d+)""".toRegex().find(name)?.value
|
||||
}
|
||||
|
||||
fun toggleReleasesExpanded(type: PatchComponent.Type) {
|
||||
releasesExpanded = !releasesExpanded
|
||||
if (releasesExpanded && releasesState is ReleasesState.Idle) {
|
||||
loadReleases(type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadReleases(type: PatchComponent.Type) = screenModelScope.launchIO {
|
||||
releasesState = ReleasesState.Loading
|
||||
github.getManagerReleases().fold(
|
||||
success = { all ->
|
||||
val assetName = assetNameFor(type)
|
||||
val filtered = all.filter { release ->
|
||||
release.assets.any { it.name == assetName }
|
||||
}
|
||||
releasesState = ReleasesState.Loaded(filtered)
|
||||
},
|
||||
fail = {
|
||||
Log.w(BuildConfig.TAG, "Failed to load GitHub releases", it)
|
||||
releasesState = ReleasesState.Failed
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fun importFromRelease(release: GithubRelease, type: PatchComponent.Type) = screenModelScope.launchIO {
|
||||
val assetName = assetNameFor(type)
|
||||
val asset = release.assets.find { it.name == assetName } ?: run {
|
||||
mainThread { context.showToast(R.string.intent_import_component_failure) }
|
||||
return@launchIO
|
||||
}
|
||||
|
||||
val targetDir = when (type) {
|
||||
PatchComponent.Type.TidalApk -> paths.customTidalApksDir
|
||||
PatchComponent.Type.Patches -> paths.customPatchesDir
|
||||
}
|
||||
targetDir.mkdirs()
|
||||
|
||||
importingReleaseTag = release.tagName
|
||||
try {
|
||||
val tempFile = targetDir.resolve("release-${System.currentTimeMillis()}.tmp")
|
||||
val result = downloader.download(asset.browserDownloadUrl, tempFile)
|
||||
if (result !is com.meowarex.rlmobile.manager.download.IDownloadManager.Result.Success) {
|
||||
tempFile.delete()
|
||||
mainThread { context.showToast(R.string.intent_import_component_failure) }
|
||||
return@launchIO
|
||||
}
|
||||
|
||||
val ext = when (type) {
|
||||
PatchComponent.Type.TidalApk -> "apk"
|
||||
PatchComponent.Type.Patches -> "zip"
|
||||
}
|
||||
val version = SemVer.parseOrNull(release.tagName.removePrefix("v"))?.toString()
|
||||
?: FALLBACK_VERSION
|
||||
val finalName = "${System.currentTimeMillis()}_$version.$ext"
|
||||
val finalFile = targetDir.resolve(finalName)
|
||||
if (!tempFile.renameTo(finalFile)) {
|
||||
tempFile.copyTo(finalFile, overwrite = true)
|
||||
tempFile.delete()
|
||||
}
|
||||
refreshComponents(type)
|
||||
mainThread { context.showToast(R.string.intent_import_component_success, finalName) }
|
||||
} catch (t: Throwable) {
|
||||
Log.e(BuildConfig.TAG, "Failed to import release ${release.tagName}", t)
|
||||
mainThread { context.showToast(R.string.intent_import_component_failure) }
|
||||
} finally {
|
||||
importingReleaseTag = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun assetNameFor(type: PatchComponent.Type) = when (type) {
|
||||
PatchComponent.Type.TidalApk -> "tidal-stock.apk"
|
||||
PatchComponent.Type.Patches -> "patches.zip"
|
||||
}
|
||||
|
||||
override fun onDispose() {
|
||||
screenModelScope.launch { setResult(selected) }
|
||||
}
|
||||
|
||||
sealed interface ReleasesState {
|
||||
data object Idle : ReleasesState
|
||||
data object Loading : ReleasesState
|
||||
data class Loaded(val releases: List<GithubRelease>) : ReleasesState
|
||||
data object Failed : ReleasesState
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FALLBACK_VERSION = "0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
+232
@@ -1,13 +1,20 @@
|
||||
package com.meowarex.rlmobile.ui.screens.componentopts
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.koin.koinScreenModel
|
||||
@@ -61,6 +68,12 @@ class ComponentOptionsScreen(
|
||||
selected = model.selected,
|
||||
onSelectComponent = model::selectComponent,
|
||||
onDeleteComponent = model::deleteComponent,
|
||||
onImportFromUri = { uri -> model.importFromUri(uri, componentType) },
|
||||
releasesExpanded = model.releasesExpanded,
|
||||
releasesState = model.releasesState,
|
||||
onToggleReleases = { model.toggleReleasesExpanded(componentType) },
|
||||
onImportRelease = { release -> model.importFromRelease(release, componentType) },
|
||||
importingReleaseTag = model.importingReleaseTag,
|
||||
onBackPressed = { navigator.back(null) },
|
||||
)
|
||||
}
|
||||
@@ -73,8 +86,22 @@ fun ComponentOptionsScreenContent(
|
||||
selected: PatchComponent?,
|
||||
onSelectComponent: (PatchComponent?) -> Unit,
|
||||
onDeleteComponent: (PatchComponent) -> Unit,
|
||||
onImportFromUri: (Uri) -> Unit,
|
||||
releasesExpanded: Boolean,
|
||||
releasesState: ComponentOptionsModel.ReleasesState,
|
||||
onToggleReleases: () -> Unit,
|
||||
onImportRelease: (com.meowarex.rlmobile.network.models.GithubRelease) -> Unit,
|
||||
importingReleaseTag: String?,
|
||||
onBackPressed: () -> Unit,
|
||||
) {
|
||||
val mime = when (componentType) {
|
||||
PatchComponent.Type.TidalApk -> "application/vnd.android.package-archive"
|
||||
PatchComponent.Type.Patches -> "application/zip"
|
||||
}
|
||||
val filePicker = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.OpenDocument(),
|
||||
) { uri -> uri?.let(onImportFromUri) }
|
||||
|
||||
Scaffold(
|
||||
topBar = { ComponentOptionsAppBar(componentType = componentType) },
|
||||
) { paddingValues ->
|
||||
@@ -98,6 +125,16 @@ fun ComponentOptionsScreenContent(
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "RELEASES_ACCORDION") {
|
||||
ReleasesAccordion(
|
||||
expanded = releasesExpanded,
|
||||
state = releasesState,
|
||||
importingTag = importingReleaseTag,
|
||||
onToggle = onToggleReleases,
|
||||
onImport = onImportRelease,
|
||||
)
|
||||
}
|
||||
|
||||
items(
|
||||
items = components,
|
||||
contentType = { "COMPONENT" },
|
||||
@@ -112,6 +149,10 @@ fun ComponentOptionsScreenContent(
|
||||
)
|
||||
}
|
||||
|
||||
item(key = "BROWSE") {
|
||||
BrowseImportCard(onClick = { filePicker.launch(arrayOf(mime)) })
|
||||
}
|
||||
|
||||
item("EXIT_BTN") {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.End,
|
||||
@@ -129,3 +170,194 @@ fun ComponentOptionsScreenContent(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReleasesAccordion(
|
||||
expanded: Boolean,
|
||||
state: ComponentOptionsModel.ReleasesState,
|
||||
importingTag: String?,
|
||||
onToggle: () -> Unit,
|
||||
onImport: (com.meowarex.rlmobile.network.models.GithubRelease) -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Column {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onToggle)
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_account_github_white_24dp),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_releases_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_releases_desc),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = 0.65f),
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
if (expanded) R.drawable.ic_arrow_up_small else R.drawable.ic_arrow_down_small
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = LocalContentColor.current.copy(alpha = 0.7f),
|
||||
)
|
||||
}
|
||||
|
||||
if (expanded) {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.35f),
|
||||
)
|
||||
ReleasesContent(
|
||||
state = state,
|
||||
importingTag = importingTag,
|
||||
onImport = onImport,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReleasesContent(
|
||||
state: ComponentOptionsModel.ReleasesState,
|
||||
importingTag: String?,
|
||||
onImport: (com.meowarex.rlmobile.network.models.GithubRelease) -> Unit,
|
||||
) {
|
||||
when (state) {
|
||||
is ComponentOptionsModel.ReleasesState.Idle,
|
||||
ComponentOptionsModel.ReleasesState.Loading -> Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(20.dp),
|
||||
) { CircularProgressIndicator(modifier = Modifier.size(24.dp)) }
|
||||
|
||||
ComponentOptionsModel.ReleasesState.Failed -> Text(
|
||||
text = stringResource(R.string.network_load_fail),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(20.dp),
|
||||
)
|
||||
|
||||
is ComponentOptionsModel.ReleasesState.Loaded -> {
|
||||
if (state.releases.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_releases_empty),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = 0.65f),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(20.dp),
|
||||
)
|
||||
} else {
|
||||
Column {
|
||||
state.releases.forEachIndexed { index, release ->
|
||||
ReleaseRow(
|
||||
release = release,
|
||||
importing = importingTag == release.tagName,
|
||||
anyImporting = importingTag != null,
|
||||
onImport = { onImport(release) },
|
||||
)
|
||||
if (index != state.releases.lastIndex) {
|
||||
HorizontalDivider(
|
||||
color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.2f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReleaseRow(
|
||||
release: com.meowarex.rlmobile.network.models.GithubRelease,
|
||||
importing: Boolean,
|
||||
anyImporting: Boolean,
|
||||
onImport: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = release.tagName,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
Text(
|
||||
text = release.name ?: release.tagName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = LocalContentColor.current.copy(alpha = 0.55f),
|
||||
)
|
||||
}
|
||||
FilledTonalButton(
|
||||
onClick = onImport,
|
||||
enabled = !anyImporting,
|
||||
) {
|
||||
if (importing) {
|
||||
CircularProgressIndicator(
|
||||
strokeWidth = 2.dp,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
} else {
|
||||
Text(stringResource(R.string.action_install))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BrowseImportCard(onClick: () -> Unit) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_add),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_browse_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.componentopts_browse_desc),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = 0.65f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -34,8 +34,8 @@ data class PatchComponent(
|
||||
@Parcelize
|
||||
@Serializable
|
||||
enum class Type : Parcelable {
|
||||
@SerialName("injector")
|
||||
Injector,
|
||||
@SerialName("tidal")
|
||||
TidalApk,
|
||||
|
||||
@SerialName("patches")
|
||||
Patches,
|
||||
@@ -47,11 +47,11 @@ data class PatchComponent(
|
||||
*/
|
||||
fun getFile(paths: PathManager): File {
|
||||
val dir = when (type) {
|
||||
Type.Injector -> paths.customInjectorsDir
|
||||
Type.TidalApk -> paths.customTidalApksDir
|
||||
Type.Patches -> paths.customPatchesDir
|
||||
}
|
||||
val ext = when (type) {
|
||||
Type.Injector -> "dex"
|
||||
Type.TidalApk -> "apk"
|
||||
Type.Patches -> "zip"
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.github.diamondminer88.zip.ZipReader
|
||||
import com.meowarex.rlmobile.BuildConfig
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.manager.PreferencesManager
|
||||
import com.meowarex.rlmobile.network.models.GithubCommit
|
||||
import com.meowarex.rlmobile.network.models.RLBuildInfo
|
||||
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
|
||||
@@ -28,6 +29,7 @@ import com.meowarex.rlmobile.patcher.InstallMetadata
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionsScreen
|
||||
import com.meowarex.rlmobile.ui.util.TidalVersion
|
||||
import com.meowarex.rlmobile.ui.widgets.managerupdate.VersionDelta
|
||||
import com.meowarex.rlmobile.util.*
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@@ -39,11 +41,15 @@ class HomeModel(
|
||||
private val application: Application,
|
||||
private val github: RadiantLyricsGithubService,
|
||||
private val json: Json,
|
||||
private val prefs: PreferencesManager,
|
||||
) : ScreenModel {
|
||||
|
||||
var state by mutableStateOf<HomeState>(HomeState.Loading)
|
||||
private set
|
||||
|
||||
var managerUpdateDeltas by mutableStateOf<List<VersionDelta>?>(null)
|
||||
private set
|
||||
|
||||
val commits = Pager(PagingConfig(pageSize = 30)) {
|
||||
CommitsPagingSource(github)
|
||||
}.flow.cachedIn(screenModelScope)
|
||||
@@ -51,10 +57,110 @@ class HomeModel(
|
||||
private val refreshingLock = Mutex()
|
||||
private var remoteDataJson: RLBuildInfo? = null
|
||||
|
||||
private val initialPrefManagerVersion: String = prefs.lastSeenManagerVersion
|
||||
private val initialPrefPatchesVersion: String = prefs.lastSeenPatchesVersion
|
||||
private val initialPrefTidalVersionCode: Int = prefs.lastSeenTidalVersionCode
|
||||
|
||||
private var managerUpdateChecked = false
|
||||
|
||||
init {
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun dismissManagerUpdate() {
|
||||
managerUpdateDeltas = null
|
||||
commitVersionPrefs()
|
||||
}
|
||||
|
||||
private fun commitVersionPrefs() {
|
||||
prefs.lastSeenManagerVersion = BuildConfig.VERSION_NAME
|
||||
remoteDataJson?.let {
|
||||
prefs.lastSeenPatchesVersion = it.patchesVersion.toString()
|
||||
prefs.lastSeenTidalVersionCode = it.tidalVersionCode
|
||||
}
|
||||
}
|
||||
|
||||
private fun maybeCheckManagerUpdate(installedPkg: PackageInfo?) {
|
||||
if (managerUpdateChecked) return
|
||||
managerUpdateChecked = true
|
||||
|
||||
val installMetadata = installedPkg?.packageName?.let(::loadInstallMetadata)
|
||||
val current = BuildConfig.VERSION_NAME
|
||||
|
||||
val previousManager = initialPrefManagerVersion.ifEmpty {
|
||||
installMetadata?.managerVersion?.toString().orEmpty()
|
||||
}
|
||||
|
||||
when {
|
||||
previousManager.isEmpty() -> commitVersionPrefs()
|
||||
previousManager == current -> commitVersionPrefs()
|
||||
else -> managerUpdateDeltas =
|
||||
buildDeltas(previousManager, current, installMetadata, installedPkg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDeltas(
|
||||
previousManager: String,
|
||||
currentManager: String,
|
||||
installMetadata: InstallMetadata?,
|
||||
installedPkg: PackageInfo?,
|
||||
): List<VersionDelta> = buildList {
|
||||
add(
|
||||
VersionDelta(
|
||||
label = application.getString(R.string.manager_update_row_manager),
|
||||
iconRes = R.drawable.ic_sparkle,
|
||||
from = previousManager,
|
||||
to = currentManager,
|
||||
tag = application.getString(R.string.manager_update_tag_complete),
|
||||
)
|
||||
)
|
||||
|
||||
val remote = remoteDataJson
|
||||
val currentPatches = remote?.patchesVersion?.toString()
|
||||
val installedPatches = installMetadata?.patchesVersion?.toString()
|
||||
val previousPatches = initialPrefPatchesVersion.ifEmpty {
|
||||
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 (patchesUpdateAvailable)
|
||||
application.getString(R.string.manager_update_tag_available) else null,
|
||||
)
|
||||
)
|
||||
|
||||
val currentTidal = remote?.tidalVersionCode
|
||||
@Suppress("DEPRECATION")
|
||||
val installedTidalVersionCode = installedPkg?.versionCode ?: -1
|
||||
val previousTidal = if (initialPrefTidalVersionCode > 0) initialPrefTidalVersionCode
|
||||
else installedTidalVersionCode
|
||||
val tidalFrom = previousTidal.takeIf { it > 0 }?.toString()
|
||||
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 (tidalUpdateAvailable)
|
||||
application.getString(R.string.manager_update_tag_available) else null,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun refresh(delay: Boolean = false) = screenModelScope.launchIO {
|
||||
if (refreshingLock.isLocked) return@launchIO
|
||||
if (delay) {
|
||||
@@ -75,6 +181,7 @@ class HomeModel(
|
||||
install = install,
|
||||
latestTidalVersionCode = latest,
|
||||
)
|
||||
maybeCheckManagerUpdate(pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +196,7 @@ class HomeModel(
|
||||
openAppInfo(current.packageName)
|
||||
}
|
||||
|
||||
fun createReinstallScreen(): PatchOptionsScreen? {
|
||||
fun createRepatchScreen(): PatchOptionsScreen? {
|
||||
val current = (state as? HomeState.Loaded)?.install ?: return null
|
||||
return createPrefilledPatchOptsScreen(current.packageName)
|
||||
}
|
||||
@@ -112,20 +219,21 @@ class HomeModel(
|
||||
}
|
||||
|
||||
fun createPrefilledPatchOptsScreen(packageName: String): PatchOptionsScreen {
|
||||
val metadata = try {
|
||||
val applicationInfo = application.packageManager.getApplicationInfo(packageName, 0)
|
||||
val metadataFile = ZipReader(applicationInfo.publicSourceDir)
|
||||
.use { it.openEntry("rlmobile.json")?.read() }
|
||||
metadataFile?.let { json.decodeFromStream<InstallMetadata>(it.inputStream()) }
|
||||
} catch (t: Throwable) {
|
||||
Log.w(BuildConfig.TAG, "Failed to parse install metadata for $packageName", t)
|
||||
null
|
||||
}
|
||||
|
||||
val patchOptions = metadata?.options ?: PatchOptions.Default.copy(packageName = packageName)
|
||||
val patchOptions = loadInstallMetadata(packageName)?.options
|
||||
?: PatchOptions.Default.copy(packageName = packageName)
|
||||
return PatchOptionsScreen(prefilledOptions = patchOptions)
|
||||
}
|
||||
|
||||
private fun loadInstallMetadata(packageName: String): InstallMetadata? = try {
|
||||
val applicationInfo = application.packageManager.getApplicationInfo(packageName, 0)
|
||||
val metadataBytes = ZipReader(applicationInfo.publicSourceDir)
|
||||
.use { it.openEntry("rlmobile.json")?.read() }
|
||||
metadataBytes?.let { json.decodeFromStream<InstallMetadata>(it.inputStream()) }
|
||||
} catch (t: Throwable) {
|
||||
Log.w(BuildConfig.TAG, "Failed to parse install metadata for $packageName", t)
|
||||
null
|
||||
}
|
||||
|
||||
private fun fetchInstalled(): PackageInfo? = application.packageManager
|
||||
.getInstalledPackages(PackageManager.GET_META_DATA)
|
||||
.firstOrNull { it.applicationInfo?.metaData?.containsKey("isRadiantLyrics") == true }
|
||||
@@ -138,7 +246,8 @@ class HomeModel(
|
||||
return InstallData(
|
||||
name = pm.getApplicationLabel(info).toString(),
|
||||
packageName = packageName,
|
||||
isUpToDate = isInstallationUpToDate(this),
|
||||
tidalUpToDate = isTidalUpToDate(this),
|
||||
patchesUpToDate = isPatchesUpToDate(this),
|
||||
icon = pm.getApplicationIcon(info).toBitmap().asImageBitmap().let(::BitmapPainter),
|
||||
version = TidalVersion.Existing(
|
||||
type = TidalVersion.parseVersionType(versionCode),
|
||||
@@ -170,11 +279,14 @@ class HomeModel(
|
||||
)
|
||||
}
|
||||
|
||||
private fun isInstallationUpToDate(pkg: PackageInfo): Boolean? {
|
||||
private fun isTidalUpToDate(pkg: PackageInfo): Boolean? {
|
||||
val remote = remoteDataJson ?: return null
|
||||
@Suppress("DEPRECATION") val versionCode = pkg.versionCode
|
||||
if (remote.tidalVersionCode != versionCode) return false
|
||||
return remote.tidalVersionCode == versionCode
|
||||
}
|
||||
|
||||
private fun isPatchesUpToDate(pkg: PackageInfo): Boolean? {
|
||||
val remote = remoteDataJson ?: return null
|
||||
val apkPath = pkg.applicationInfo?.publicSourceDir ?: return false
|
||||
val installMetadata = try {
|
||||
val mf = ZipReader(apkPath).use { it.openEntry("rlmobile.json")?.read() } ?: return false
|
||||
@@ -182,8 +294,6 @@ class HomeModel(
|
||||
} catch (t: Throwable) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (installMetadata.options.customPatches != null) return true
|
||||
return remote.patchesVersion == installMetadata.patchesVersion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -29,14 +30,17 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.meowarex.rlmobile.R
|
||||
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 {
|
||||
@@ -48,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)
|
||||
@@ -65,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),
|
||||
@@ -105,10 +114,11 @@ class HomeScreen : Screen, Parcelable {
|
||||
is HomeState.Loaded -> HomeContent(
|
||||
state = state,
|
||||
commits = model.commits,
|
||||
managerUpdateAvailable = managerUpdateAvailable,
|
||||
onInstall = { navigator.pushOnce(PatchOptionsScreen()) },
|
||||
onReinstall = {
|
||||
onRepatch = {
|
||||
scope.launchIO {
|
||||
val screen = model.createReinstallScreen() ?: return@launchIO
|
||||
val screen = model.createRepatchScreen() ?: return@launchIO
|
||||
mainThread { navigator.push(screen) }
|
||||
}
|
||||
},
|
||||
@@ -116,6 +126,13 @@ class HomeScreen : Screen, Parcelable {
|
||||
onInfo = model::openCurrentAppInfo,
|
||||
)
|
||||
}
|
||||
|
||||
model.managerUpdateDeltas?.let { deltas ->
|
||||
ManagerUpdateDialog(
|
||||
deltas = deltas,
|
||||
onDismiss = model::dismissManagerUpdate,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,13 +142,15 @@ 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,
|
||||
onReinstall: () -> Unit,
|
||||
onRepatch: () -> Unit,
|
||||
onLaunch: () -> Unit,
|
||||
onInfo: () -> Unit,
|
||||
) {
|
||||
val install = state.install
|
||||
val currentVersionName = install?.version?.let { "v${it.toString()}" }
|
||||
val currentVersionName = (install?.version as? com.meowarex.rlmobile.ui.util.TidalVersion.Existing)
|
||||
?.let { "v${it.name} (build ${it.code})" }
|
||||
val latestVersionName = state.latestTidalVersionCode?.let { "build $it" }
|
||||
|
||||
val fallbackPainter = if (install?.icon == null) {
|
||||
@@ -181,16 +200,29 @@ private fun ColumnScope.HomeContent(
|
||||
}
|
||||
}
|
||||
|
||||
val patchesBehind = install != null && install.patchesUpToDate == false
|
||||
val tidalBehind = install != null && install.tidalUpToDate == false
|
||||
AnimatedVisibility(visible = patchesBehind || tidalBehind) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
if (patchesBehind) Tag(text = "New Patches!")
|
||||
if (tidalBehind) Tag(text = "TIDAL Update!")
|
||||
}
|
||||
}
|
||||
|
||||
val blockedByManagerUpdate = managerUpdateAvailable && (patchesBehind || tidalBehind)
|
||||
Button(
|
||||
onClick = if (install == null) onInstall else onReinstall,
|
||||
enabled = state.latestTidalVersionCode != null,
|
||||
onClick = if (install == null) onInstall else onRepatch,
|
||||
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"
|
||||
else -> "Reinstall"
|
||||
patchesBehind && tidalBehind -> "Update Patches & TIDAL"
|
||||
patchesBehind -> "Update Patches"
|
||||
tidalBehind -> "Update TIDAL"
|
||||
else -> "Repatch"
|
||||
}
|
||||
Text(
|
||||
text = label,
|
||||
|
||||
@@ -10,5 +10,12 @@ data class InstallData(
|
||||
val packageName: String,
|
||||
val version: TidalVersion,
|
||||
val icon: BitmapPainter,
|
||||
val isUpToDate: Boolean?,
|
||||
)
|
||||
val tidalUpToDate: Boolean?,
|
||||
val patchesUpToDate: Boolean?,
|
||||
) {
|
||||
val isUpToDate: Boolean?
|
||||
get() = when {
|
||||
tidalUpToDate == null || patchesUpToDate == null -> null
|
||||
else -> tidalUpToDate && patchesUpToDate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
-14
@@ -28,7 +28,6 @@ import com.meowarex.rlmobile.patcher.steps.StepGroup
|
||||
import com.meowarex.rlmobile.ui.components.MainActionButton
|
||||
import com.meowarex.rlmobile.ui.components.Wakelock
|
||||
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.patching.components.*
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptions
|
||||
@@ -111,19 +110,6 @@ class PatchingScreen(
|
||||
listState.animateScrollToItem(0)
|
||||
}
|
||||
|
||||
if (model.showNetworkWarningDialog) {
|
||||
NetworkWarningDialog(
|
||||
onConfirm = { neverShow ->
|
||||
model.hideNetworkWarning(neverShow)
|
||||
model.install()
|
||||
},
|
||||
onDismiss = { neverShow ->
|
||||
model.hideNetworkWarning(neverShow)
|
||||
navigator.pop()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (showAbortWarning) {
|
||||
InstallerAbortDialog(
|
||||
onDismiss = { showAbortWarning = false },
|
||||
|
||||
+1
-17
@@ -41,9 +41,6 @@ class PatchingScreenModel(
|
||||
|
||||
val devMode get() = prefs.devMode
|
||||
|
||||
var showNetworkWarningDialog by mutableStateOf(!alreadyShownNetworkWarning && application.isNetworkDangerous())
|
||||
private set
|
||||
|
||||
var steps by mutableStateOf<ImmutableMap<StepGroup, ImmutableList<Step>>?>(null)
|
||||
private set
|
||||
|
||||
@@ -52,11 +49,7 @@ class PatchingScreenModel(
|
||||
private set
|
||||
|
||||
init {
|
||||
if (!prefs.showNetworkWarning)
|
||||
showNetworkWarningDialog = false
|
||||
|
||||
if (!showNetworkWarningDialog)
|
||||
install()
|
||||
install()
|
||||
|
||||
// Rotate fun facts every so often
|
||||
screenModelScope.launch {
|
||||
@@ -67,12 +60,6 @@ class PatchingScreenModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun hideNetworkWarning(neverShow: Boolean) {
|
||||
showNetworkWarningDialog = false
|
||||
alreadyShownNetworkWarning = true
|
||||
prefs.showNetworkWarning = !neverShow
|
||||
}
|
||||
|
||||
fun launchApp() {
|
||||
if (state.value !is PatchingScreenState.Success)
|
||||
return
|
||||
@@ -194,9 +181,6 @@ class PatchingScreenModel(
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
+83
-4
@@ -3,20 +3,39 @@ package com.meowarex.rlmobile.ui.screens.patchopts
|
||||
import androidx.annotation.StringRes
|
||||
import com.meowarex.rlmobile.R
|
||||
|
||||
data class PatchVariant(
|
||||
@StringRes val titleRes: Int,
|
||||
val fileNames: List<String>,
|
||||
)
|
||||
|
||||
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(),
|
||||
val variants: List<PatchVariant> = emptyList(),
|
||||
val defaultVariantIndex: Int = 0,
|
||||
) {
|
||||
// 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 +44,81 @@ 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),
|
||||
),
|
||||
MiniPlayerRedesign(
|
||||
order = 50,
|
||||
fileNames = emptyList(),
|
||||
titleRes = R.string.patch_mini_player_redesign_title,
|
||||
descRes = R.string.patch_mini_player_redesign_desc,
|
||||
defaultVariantIndex = 2,
|
||||
variants = listOf(
|
||||
PatchVariant(
|
||||
titleRes = R.string.patch_mini_player_variant_floating_title,
|
||||
fileNames = listOf("mini-player-floating.patch"),
|
||||
),
|
||||
PatchVariant(
|
||||
titleRes = R.string.patch_mini_player_variant_square_grey_title,
|
||||
fileNames = listOf("mini-player-grey.patch"),
|
||||
),
|
||||
PatchVariant(
|
||||
titleRes = R.string.patch_mini_player_variant_square_black_title,
|
||||
fileNames = listOf("mini-player-black.patch"),
|
||||
),
|
||||
),
|
||||
),
|
||||
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 +127,24 @@ enum class KnownPatch(
|
||||
LyricsDisableCover,
|
||||
LyricsReplaceLyricsButton,
|
||||
LyricsReplaceShareButton,
|
||||
LyricsRlApi,
|
||||
LyricsKeepControlsVisible,
|
||||
PlayerBackdrop,
|
||||
LyricsProgressPill,
|
||||
CoverEverywhere,
|
||||
),
|
||||
);
|
||||
|
||||
val allVariantFileNames: Set<String>
|
||||
get() = variants.flatMapTo(mutableSetOf()) { it.fileNames }
|
||||
|
||||
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.firstOrNull() ?: it.name })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+20
-12
@@ -25,10 +25,7 @@ data class PatchOptions(
|
||||
*/
|
||||
val debuggable: Boolean,
|
||||
|
||||
/**
|
||||
* A custom build of injector that was used rather than the latest.
|
||||
*/
|
||||
val customInjector: PatchComponent? = null,
|
||||
val customTidalApk: PatchComponent? = null,
|
||||
|
||||
/**
|
||||
* A custom smali patches bundle that was used rather than the latest.
|
||||
@@ -36,15 +33,26 @@ data class PatchOptions(
|
||||
val customPatches: PatchComponent? = null,
|
||||
|
||||
val disabledPatches: Set<String> = emptySet(),
|
||||
|
||||
val selectedVariants: Map<String, Int> = emptyMap(),
|
||||
) : Parcelable {
|
||||
companion object {
|
||||
val Default = PatchOptions(
|
||||
appName = "TIDAL",
|
||||
packageName = "com.aspiro.tidal",
|
||||
debuggable = false,
|
||||
customInjector = null,
|
||||
customPatches = null,
|
||||
disabledPatches = emptySet(),
|
||||
)
|
||||
val Default: PatchOptions = run {
|
||||
val miniPlayerFiles = KnownPatch.MiniPlayerRedesign.allVariantFileNames
|
||||
val disabled = (
|
||||
KnownPatch.DebugMenuUnlock.fileNames +
|
||||
KnownPatch.EnableLegacyUi.fileNames +
|
||||
miniPlayerFiles
|
||||
).toSet()
|
||||
|
||||
PatchOptions(
|
||||
appName = "TIDAL",
|
||||
packageName = "com.aspiro.tidal",
|
||||
debuggable = false,
|
||||
customTidalApk = null,
|
||||
customPatches = null,
|
||||
disabledPatches = disabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+77
-8
@@ -50,10 +50,32 @@ class PatchOptionsModel(
|
||||
var disabledPatches by mutableStateOf(prefilledOptions.disabledPatches)
|
||||
private set
|
||||
|
||||
fun isPatchEnabled(patch: KnownPatch): Boolean =
|
||||
patch.fileNames.none { it in disabledPatches }
|
||||
var selectedVariants by mutableStateOf(prefilledOptions.selectedVariants)
|
||||
private set
|
||||
|
||||
fun variantIndex(patch: KnownPatch): Int = selectedVariants[patch.name]
|
||||
?.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0))
|
||||
?: patch.defaultVariantIndex.coerceIn(0, patch.variants.lastIndex.coerceAtLeast(0))
|
||||
|
||||
fun isPatchEnabled(patch: KnownPatch): Boolean = if (patch.variants.isNotEmpty()) {
|
||||
val v = patch.variants[variantIndex(patch)]
|
||||
v.fileNames.isNotEmpty() && v.fileNames.none { it in disabledPatches }
|
||||
} else {
|
||||
patch.fileNames.isNotEmpty() && patch.fileNames.none { it in disabledPatches }
|
||||
}
|
||||
|
||||
fun setPatchEnabled(patch: KnownPatch, enabled: Boolean) {
|
||||
if (patch.variants.isNotEmpty()) {
|
||||
val all = patch.allVariantFileNames
|
||||
val selected = patch.variants[variantIndex(patch)].fileNames.toSet()
|
||||
disabledPatches = if (enabled) {
|
||||
(disabledPatches + all) - selected
|
||||
} else {
|
||||
disabledPatches + all
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fun closure(seed: KnownPatch, step: (KnownPatch) -> List<KnownPatch>): Set<KnownPatch> =
|
||||
buildSet {
|
||||
fun walk(p: KnownPatch) { if (add(p)) step(p).forEach(::walk) }
|
||||
@@ -78,19 +100,50 @@ class PatchOptionsModel(
|
||||
disabledPatches = (disabledPatches - enableFiles) + disableFiles
|
||||
}
|
||||
|
||||
fun selectVariant(patch: KnownPatch, index: Int) {
|
||||
if (patch.variants.isEmpty() || index !in patch.variants.indices) return
|
||||
val wasOn = isPatchEnabled(patch)
|
||||
selectedVariants = selectedVariants + (patch.name to index)
|
||||
if (wasOn) setPatchEnabled(patch, true)
|
||||
}
|
||||
|
||||
fun lockState(patch: KnownPatch): PatchLock {
|
||||
if (patch.variants.isNotEmpty()) return PatchLock.Free
|
||||
|
||||
fun closure(seed: KnownPatch, step: (KnownPatch) -> List<KnownPatch>): Set<KnownPatch> =
|
||||
buildSet {
|
||||
fun walk(p: KnownPatch) { if (add(p)) step(p).forEach(::walk) }
|
||||
walk(seed)
|
||||
}
|
||||
|
||||
for (other in KnownPatch.All) {
|
||||
if (other == patch || !isPatchEnabled(other)) continue
|
||||
|
||||
val requiresClosure = closure(other) { it.requires }
|
||||
if (patch in requiresClosure - other) return PatchLock.LockedOn(other)
|
||||
|
||||
val disablesClosure = requiresClosure.flatMap { it.disables }
|
||||
.flatMapTo(mutableSetOf()) { d ->
|
||||
closure(d) { dep -> KnownPatch.All.filter { dep in it.requires } }
|
||||
}
|
||||
if (patch in disablesClosure) return PatchLock.LockedOff(other)
|
||||
}
|
||||
return PatchLock.Free
|
||||
}
|
||||
|
||||
val enabledPatchCount: Int
|
||||
get() = KnownPatch.All.count { isPatchEnabled(it) }
|
||||
|
||||
var customInjector by mutableStateOf<PatchComponent?>(null)
|
||||
var customTidalApk by mutableStateOf<PatchComponent?>(null)
|
||||
private set
|
||||
var customPatches by mutableStateOf<PatchComponent?>(null)
|
||||
private set
|
||||
|
||||
fun selectCustomInjector(navigator: Navigator) = screenModelScope.launch {
|
||||
customInjector = navigator.pushForResult(
|
||||
fun selectCustomTidalApk(navigator: Navigator) = screenModelScope.launch {
|
||||
customTidalApk = navigator.pushForResult(
|
||||
ComponentOptionsScreen(
|
||||
default = customInjector,
|
||||
componentType = PatchComponent.Type.Injector,
|
||||
default = customTidalApk,
|
||||
componentType = PatchComponent.Type.TidalApk,
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -120,9 +173,10 @@ class PatchOptionsModel(
|
||||
appName = appName,
|
||||
packageName = packageName,
|
||||
debuggable = debuggable,
|
||||
customInjector = customInjector,
|
||||
customTidalApk = customTidalApk,
|
||||
customPatches = customPatches,
|
||||
disabledPatches = disabledPatches,
|
||||
selectedVariants = selectedVariants,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -147,7 +201,16 @@ class PatchOptionsModel(
|
||||
mainThread { packageNameState = state }
|
||||
}
|
||||
|
||||
private fun validatePatchSelection() {
|
||||
for (patch in KnownPatch.All) {
|
||||
if (isPatchEnabled(patch)) {
|
||||
setPatchEnabled(patch, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
validatePatchSelection()
|
||||
screenModelScope.launchBlock { fetchPkgNameState() }
|
||||
}
|
||||
|
||||
@@ -162,3 +225,9 @@ enum class PackageNameState {
|
||||
Invalid,
|
||||
Taken,
|
||||
}
|
||||
|
||||
sealed class PatchLock {
|
||||
object Free : PatchLock()
|
||||
data class LockedOn(val by: KnownPatch) : PatchLock()
|
||||
data class LockedOff(val by: KnownPatch) : PatchLock()
|
||||
}
|
||||
|
||||
+19
-10
@@ -55,14 +55,17 @@ class PatchOptionsScreen(
|
||||
packageNameState = model.packageNameState,
|
||||
setPackageName = model::changePackageName,
|
||||
|
||||
customInjector = model.customInjector,
|
||||
customTidalApk = model.customTidalApk,
|
||||
customPatches = model.customPatches,
|
||||
onSelectCustomInjector = { model.selectCustomInjector(navigator) },
|
||||
onSelectCustomTidalApk = { model.selectCustomTidalApk(navigator) },
|
||||
onSelectCustomPatches = { model.selectCustomPatches(navigator) },
|
||||
|
||||
enabledPatchCount = model.enabledPatchCount,
|
||||
isPatchEnabled = model::isPatchEnabled,
|
||||
onTogglePatch = model::setPatchEnabled,
|
||||
patchLockState = model::lockState,
|
||||
variantIndex = model::variantIndex,
|
||||
onSelectVariant = model::selectVariant,
|
||||
|
||||
isConfigValid = model.isConfigValid,
|
||||
onInstall = {
|
||||
@@ -88,14 +91,17 @@ fun PatchOptionsScreenContent(
|
||||
packageNameState: PackageNameState,
|
||||
setPackageName: (String) -> Unit,
|
||||
|
||||
customInjector: PatchComponent?,
|
||||
onSelectCustomInjector: () -> Unit,
|
||||
customTidalApk: PatchComponent?,
|
||||
onSelectCustomTidalApk: () -> Unit,
|
||||
customPatches: PatchComponent?,
|
||||
onSelectCustomPatches: () -> Unit,
|
||||
|
||||
enabledPatchCount: Int,
|
||||
isPatchEnabled: (KnownPatch) -> Boolean,
|
||||
onTogglePatch: (KnownPatch, Boolean) -> Unit,
|
||||
patchLockState: (KnownPatch) -> PatchLock,
|
||||
variantIndex: (KnownPatch) -> Int,
|
||||
onSelectVariant: (KnownPatch, Int) -> Unit,
|
||||
|
||||
isConfigValid: Boolean,
|
||||
onInstall: () -> Unit,
|
||||
@@ -162,6 +168,9 @@ fun PatchOptionsScreenContent(
|
||||
totalCount = KnownPatch.All.size,
|
||||
isEnabled = isPatchEnabled,
|
||||
onToggle = onTogglePatch,
|
||||
lockState = patchLockState,
|
||||
variantIndex = variantIndex,
|
||||
onSelectVariant = onSelectVariant,
|
||||
modifier = Modifier.padding(top = 4.dp),
|
||||
)
|
||||
|
||||
@@ -180,14 +189,14 @@ fun PatchOptionsScreenContent(
|
||||
)
|
||||
|
||||
IconPatchOption(
|
||||
icon = painterResource(R.drawable.ic_extension),
|
||||
name = stringResource(R.string.patchopts_custom_injector_title),
|
||||
description = stringResource(R.string.patchopts_custom_injector_desc),
|
||||
modifier = Modifier.clickable(onClick = onSelectCustomInjector),
|
||||
icon = painterResource(R.drawable.ic_music_note),
|
||||
name = stringResource(R.string.patchopts_custom_tidal_apk_title),
|
||||
description = stringResource(R.string.patchopts_custom_tidal_apk_desc),
|
||||
modifier = Modifier.clickable(onClick = onSelectCustomTidalApk),
|
||||
) {
|
||||
FilledTonalButton(onClick = onSelectCustomInjector) {
|
||||
FilledTonalButton(onClick = onSelectCustomTidalApk) {
|
||||
Text(
|
||||
text = customInjector?.version?.toString()
|
||||
text = customTidalApk?.version?.toString()
|
||||
?: stringResource(R.string.componentopts_selected_none)
|
||||
)
|
||||
}
|
||||
|
||||
+124
-13
@@ -18,9 +18,14 @@ import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.fromHtml
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.KnownPatch
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchLock
|
||||
|
||||
private data class LockInfo(val patch: KnownPatch, val lock: PatchLock)
|
||||
|
||||
@Composable
|
||||
fun PatchSelectionAccordion(
|
||||
@@ -28,6 +33,9 @@ fun PatchSelectionAccordion(
|
||||
totalCount: Int,
|
||||
isEnabled: (KnownPatch) -> Boolean,
|
||||
onToggle: (KnownPatch, Boolean) -> Unit,
|
||||
lockState: (KnownPatch) -> PatchLock,
|
||||
variantIndex: (KnownPatch) -> Int,
|
||||
onSelectVariant: (KnownPatch, Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var expanded by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -36,6 +44,8 @@ fun PatchSelectionAccordion(
|
||||
label = "patch-accordion-arrow",
|
||||
)
|
||||
|
||||
var lockInfo by remember { mutableStateOf<LockInfo?>(null) }
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
@@ -80,6 +90,7 @@ fun PatchSelectionAccordion(
|
||||
|
||||
AnimatedVisibility(visible = expanded) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.background.copy(0.4f))
|
||||
@@ -94,45 +105,73 @@ fun PatchSelectionAccordion(
|
||||
)
|
||||
|
||||
for (patch in KnownPatch.All) key(patch) {
|
||||
PatchCheckboxRow(
|
||||
val checked = isEnabled(patch)
|
||||
val lock = lockState(patch)
|
||||
PatchSwitchRow(
|
||||
title = stringResource(patch.titleRes),
|
||||
description = stringResource(patch.descRes),
|
||||
checked = isEnabled(patch),
|
||||
checked = checked,
|
||||
lock = lock,
|
||||
onCheckedChange = { onToggle(patch, it) },
|
||||
onLockedTap = { lockInfo = LockInfo(patch, lock) },
|
||||
)
|
||||
|
||||
if (patch.variants.isNotEmpty()) {
|
||||
AnimatedVisibility(visible = checked) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 4.dp, end = 4.dp, top = 4.dp, bottom = 4.dp),
|
||||
) {
|
||||
PatchVariantSelector(
|
||||
variants = patch.variants,
|
||||
selectedIndex = variantIndex(patch),
|
||||
onSelect = { idx -> onSelectVariant(patch, idx) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lockInfo?.let { info ->
|
||||
PatchLockDialog(
|
||||
thisPatch = info.patch,
|
||||
lock = info.lock,
|
||||
onDismiss = { lockInfo = null },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PatchCheckboxRow(
|
||||
private fun PatchSwitchRow(
|
||||
title: String,
|
||||
description: String,
|
||||
checked: Boolean,
|
||||
lock: PatchLock,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
onLockedTap: () -> Unit,
|
||||
) {
|
||||
val interactionSource = remember(::MutableInteractionSource)
|
||||
val isLocked = lock !is PatchLock.Free
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
role = Role.Checkbox,
|
||||
) { onCheckedChange(!checked) }
|
||||
.padding(vertical = 4.dp),
|
||||
role = Role.Switch,
|
||||
) {
|
||||
if (isLocked) onLockedTap() else onCheckedChange(!checked)
|
||||
}
|
||||
.alpha(if (isLocked) 0.45f else 1f)
|
||||
.padding(vertical = 6.dp),
|
||||
) {
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
modifier = Modifier.weight(1f),
|
||||
@@ -147,5 +186,77 @@ private fun PatchCheckboxRow(
|
||||
modifier = Modifier.alpha(.7f),
|
||||
)
|
||||
}
|
||||
|
||||
Box {
|
||||
Switch(
|
||||
checked = checked,
|
||||
enabled = !isLocked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
if (isLocked) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.clickable(
|
||||
interactionSource = remember(::MutableInteractionSource),
|
||||
indication = null,
|
||||
role = Role.Switch,
|
||||
) { onLockedTap() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun PatchLockDialog(
|
||||
thisPatch: KnownPatch,
|
||||
lock: PatchLock,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val (titleRes, msgRes, blockerTitle) = when (lock) {
|
||||
is PatchLock.LockedOn -> Triple(
|
||||
R.string.patch_lock_required_title,
|
||||
R.string.patch_lock_required_msg,
|
||||
stringResource(lock.by.titleRes),
|
||||
)
|
||||
is PatchLock.LockedOff -> Triple(
|
||||
R.string.patch_lock_blocked_title,
|
||||
R.string.patch_lock_blocked_msg,
|
||||
stringResource(lock.by.titleRes),
|
||||
)
|
||||
PatchLock.Free -> return
|
||||
}
|
||||
|
||||
BasicAlertDialog(onDismissRequest = onDismiss) {
|
||||
Surface(
|
||||
shape = MaterialTheme.shapes.large,
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
tonalElevation = 6.dp,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 24.dp, end = 12.dp, top = 20.dp, bottom = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(titleRes),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
)
|
||||
Text(
|
||||
text = AnnotatedString.fromHtml(stringResource(msgRes, blockerTitle)),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
TextButton(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
) {
|
||||
Text(stringResource(R.string.action_got_it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.meowarex.rlmobile.ui.screens.patchopts.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import com.meowarex.rlmobile.ui.screens.patchopts.PatchVariant
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PatchVariantSelector(
|
||||
variants: List<PatchVariant>,
|
||||
selectedIndex: Int,
|
||||
onSelect: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SingleChoiceSegmentedButtonRow(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
variants.forEachIndexed { index, variant ->
|
||||
SegmentedButton(
|
||||
selected = index == selectedIndex,
|
||||
onClick = { onSelect(index) },
|
||||
shape = SegmentedButtonDefaults.itemShape(
|
||||
index = index,
|
||||
count = variants.size,
|
||||
),
|
||||
icon = {},
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(variant.titleRes),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package com.meowarex.rlmobile.ui.widgets.managerupdate
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.meowarex.rlmobile.R
|
||||
import com.meowarex.rlmobile.ui.components.Tag
|
||||
|
||||
data class VersionDelta(
|
||||
val label: String,
|
||||
@DrawableRes val iconRes: Int,
|
||||
val from: String?,
|
||||
val to: String,
|
||||
val tag: String? = null,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ManagerUpdateDialog(
|
||||
deltas: List<VersionDelta>,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
confirmButton = {
|
||||
FilledTonalButton(
|
||||
onClick = onDismiss,
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
),
|
||||
) {
|
||||
Text(stringResource(R.string.action_continue))
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.manager_update_title),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.manager_update_subtitle),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
deltas.forEach { delta ->
|
||||
DeltaCard(delta = delta)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_update),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(36.dp),
|
||||
)
|
||||
},
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = true,
|
||||
dismissOnClickOutside = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeltaCard(delta: VersionDelta) {
|
||||
val changed = delta.from != null && delta.from != delta.to
|
||||
val subtitle = when {
|
||||
delta.from == null || delta.from == delta.to -> delta.to
|
||||
else -> "${delta.from} → ${delta.to}"
|
||||
}
|
||||
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
modifier = Modifier.padding(horizontal = 14.dp, vertical = 12.dp),
|
||||
) {
|
||||
DeltaIcon(delta)
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = delta.label,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = if (changed) MaterialTheme.colorScheme.primary
|
||||
else LocalContentColor.current.copy(alpha = 0.65f),
|
||||
fontWeight = if (changed) FontWeight.SemiBold else FontWeight.Normal,
|
||||
)
|
||||
}
|
||||
delta.tag?.let { Tag(text = it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DeltaIcon(delta: VersionDelta) {
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.secondaryContainer,
|
||||
modifier = Modifier.size(40.dp),
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
|
||||
Icon(
|
||||
painter = painterResource(delta.iconRes),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
modifier = Modifier.size(22.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+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
|
||||
|
||||
@@ -5,11 +5,9 @@ import android.app.Activity
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
import android.provider.Settings
|
||||
import android.telephony.TelephonyManager
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
android:pathData="M5.209,1.737C5.313,1.473 5.687,1.473 5.791,1.737L6.157,2.667C6.189,2.747 6.253,2.811 6.333,2.843L7.263,3.209C7.527,3.313 7.527,3.687 7.263,3.791L6.333,4.157C6.253,4.189 6.189,4.253 6.157,4.333L5.791,5.263C5.687,5.527 5.313,5.527 5.209,5.263L4.843,4.333C4.811,4.253 4.747,4.189 4.667,4.157L3.737,3.791C3.473,3.687 3.473,3.313 3.737,3.209L4.667,2.843C4.747,2.811 4.811,2.747 4.843,2.667L5.209,1.737Z" />
|
||||
</group>
|
||||
</vector>
|
||||
aight
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M19,3L9,5v9.85c-0.59,-0.34 -1.27,-0.55 -2,-0.55c-2.21,0 -4,1.79 -4,4s1.79,4 4,4s4,-1.79 4,-4V7.36l8,-1.6v6.49c-0.59,-0.34 -1.27,-0.55 -2,-0.55c-2.21,0 -4,1.79 -4,4s1.79,4 4,4s4,-1.79 4,-4V3z" />
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M120,880v-800l60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60v800l-60,-60 -60,60 -60,-60 -60,60 -60,-60 -60,60 -60,-60 -60,60 -60,-60 -60,60 -60,-60 -60,60ZM240,680h480v-80L240,600v80ZM240,520h480v-80L240,440v80ZM240,360h480v-80L240,280v80Z" />
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="m819,932 l-59,-59q-10,3 -19.5,5T720,880L240,880q-50,0 -85,-35t-35,-85v-120h120v-287L27,140l57,-57L876,875l-57,57ZM840,727 L760,647v-447L313,200l-73,-73v-47l60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60 60,60 60,-60v647ZM320,640h207L320,433v207ZM475,360 L395,280h205v80L475,360ZM595,480 L515,400h85v80h-5ZM680,480q-17,0 -28.5,-11.5T640,440q0,-17 11.5,-28.5T680,400q17,0 28.5,11.5T720,440q0,17 -11.5,28.5T680,480ZM680,360q-17,0 -28.5,-11.5T640,320q0,-17 11.5,-28.5T680,280q17,0 28.5,11.5T720,320q0,17 -11.5,28.5T680,360Z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M19,9l1.25,-2.75L23,5l-2.75,-1.25L19,1l-1.25,2.75L15,5l2.75,1.25L19,9zm-7.5,0.5L9,4 6.5,9.5 1,12l5.5,2.5L9,20l2.5,-5.5L17,12l-5.5,-2.5zM19,15l-1.25,2.75L15,19l2.75,1.25L19,23l1.25,-2.75L23,19l-2.75,-1.25z" />
|
||||
</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>
|
||||
|
||||
@@ -43,10 +43,18 @@
|
||||
<string name="action_open_info">Open Info</string>
|
||||
<string name="action_reset_default">Reset to default</string>
|
||||
|
||||
<string name="intent_reinstall_fail">Failed to automatically reinstall! Please try doing it manually.</string>
|
||||
<string name="intent_reinstall_fail">Failed to automatically repatch! Please try doing it manually.</string>
|
||||
<string name="intent_import_component_success">Successfully imported %s</string>
|
||||
<string name="intent_import_component_failure">Failed to import custom component!</string>
|
||||
|
||||
<string name="manager_update_title">Update Complete</string>
|
||||
<string name="manager_update_subtitle">Manager was successfully updated!</string>
|
||||
<string name="manager_update_row_manager">Manager</string>
|
||||
<string name="manager_update_row_patches">Patches</string>
|
||||
<string name="manager_update_row_tidal">TIDAL</string>
|
||||
<string name="manager_update_tag_complete">Complete</string>
|
||||
<string name="manager_update_tag_available">Available</string>
|
||||
|
||||
<string name="permissions_title">Grant Permissions</string>
|
||||
<string name="permissions_subtitle">Radiant Lyrics Manager requires permissions:</string>
|
||||
<string name="permissions_legend">%s indicates required permissions!</string>
|
||||
@@ -63,7 +71,7 @@
|
||||
<string name="permissions_notifs_title">Notifications</string>
|
||||
<string name="permissions_battery_title">Background Battery</string>
|
||||
<string name="permissions_battery_desc">Ensures the installation process does not get automatically cancelled if the app is minimized.</string>
|
||||
<string name="permissions_notifs_desc">Used only to show the download progress if Radiant Lyrics Manager is minimized during installation.</string>
|
||||
<string name="permissions_notifs_desc">Used to notify you when updates are available!</string>
|
||||
|
||||
<string name="permissions_root_denied">Failed to obtain root permissions</string>
|
||||
<string name="permissions_shizuku_denied">Failed to obtain Shizuku permissions</string>
|
||||
@@ -121,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>
|
||||
|
||||
@@ -235,10 +242,10 @@
|
||||
>The app name is what\'s displayed in your home launcher. This should be changed on secondary installations for ease of use.</string>
|
||||
<string name="patchopts_debuggable_title">Debuggable</string>
|
||||
<string name="patchopts_debuggable_desc">Enable the debuggable manifest flag. Only use this if you know what you are doing!</string>
|
||||
<string name="patchopts_custom_injector_title">Custom Injector</string>
|
||||
<string name="patchopts_custom_injector_desc">A custom injector build that was imported by Manager.</string>
|
||||
<string name="patchopts_custom_tidal_apk_title">Custom TIDAL APK</string>
|
||||
<string name="patchopts_custom_tidal_apk_desc">Provide your own Stock TIDAL APK to patch instead of the version from Github.</string>
|
||||
<string name="patchopts_custom_patches_title">Custom Patches</string>
|
||||
<string name="patchopts_custom_patches_desc">A custom smali patch bundle that was imported by Manager.</string>
|
||||
<string name="patchopts_custom_patches_desc">Provide your own Patches Zip to use instead of the ones from Github.</string>
|
||||
<string name="patchopts_divider_basic">Basic</string>
|
||||
<string name="patchopts_divider_advanced">Advanced</string>
|
||||
<string name="patchopts_patches_title">Patches</string>
|
||||
@@ -251,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"
|
||||
@@ -266,10 +283,27 @@
|
||||
name="patch_enable_legacy_ui_desc"
|
||||
>[This patch will stop working soon] - Replaces the New Compose based UI with the Legacy UI (disables player-market-ui feature flag)</string>
|
||||
|
||||
<string name="patch_mini_player_redesign_title">Redesigned Mini-Player</string>
|
||||
<string name="patch_mini_player_redesign_desc">Integrates the Seekbar into the control panel at the bottom of the screen.</string>
|
||||
<string name="patch_mini_player_variant_floating_title">Floating</string>
|
||||
<string name="patch_mini_player_variant_square_grey_title">Grey</string>
|
||||
<string name="patch_mini_player_variant_square_black_title">Black</string>
|
||||
|
||||
<string name="patch_lock_required_title">Patch Required!</string>
|
||||
<string name="patch_lock_blocked_title">Patch Blocked!</string>
|
||||
<string name="patch_lock_required_msg">Patch Required by <b>%s</b>.</string>
|
||||
<string name="patch_lock_blocked_msg">Patch Blocked by <b>%s</b>.</string>
|
||||
<string name="action_got_it">Got it</string>
|
||||
|
||||
<string name="componentopts_screen_title">Custom Component (%s)</string>
|
||||
<string name="componentopts_screen_desc">Select a custom build that was imported by Manager.</string>
|
||||
<string name="componentopts_selected_none">Latest</string>
|
||||
<string name="componentopts_deleted">Successfully deleted component!</string>
|
||||
<string name="componentopts_browse_title">Browse files…</string>
|
||||
<string name="componentopts_browse_desc">Import a file from your device</string>
|
||||
<string name="componentopts_releases_title">GitHub Releases</string>
|
||||
<string name="componentopts_releases_desc">Pick a historical release from the repo</string>
|
||||
<string name="componentopts_releases_empty">No releases with this component found</string>
|
||||
|
||||
<string name="log_title">Log</string>
|
||||
<string name="log_section_install_info">Installation Info</string>
|
||||
@@ -280,13 +314,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>
|
||||
@@ -302,7 +329,7 @@
|
||||
<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="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_2">Radiant Lyrics is also available for DESKTOP!!!</string>
|
||||
<string name="fun_fact_3">i am in your walls!</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
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"tidalVersionCode": 9089,
|
||||
"tidalVersionCode": 9090,
|
||||
"tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk",
|
||||
"patchesVersion": "0.5.0"
|
||||
"patchesVersion": "0.9.7"
|
||||
}
|
||||
|
||||
@@ -1,43 +1,36 @@
|
||||
--- a/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
|
||||
.locals 2
|
||||
|
||||
+ # rl-debug-unlock: force a() to always return true, bypassing the
|
||||
+ # "debug-menu" feature flag check that normally hides the Settings entry.
|
||||
+ const/4 v0, 0x1
|
||||
+ return v0
|
||||
+ const/4 v0, 0x1 # force true
|
||||
+ return v0 # bypass debug flag
|
||||
+
|
||||
.line 1
|
||||
iget-object v0, p0, Lcom/tidal/android/core/debug/DebugFeatureInteractorDefault;->a:Lcom/tidal/android/featureflags/l;
|
||||
|
||||
@@ -97,6 +102,14 @@
|
||||
@@ -98,6 +101,14 @@
|
||||
}
|
||||
.end annotation
|
||||
|
||||
+ # rl-debug-unlock: short-circuit b() to always emit MutableStateFlow(TRUE).
|
||||
+ # 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;
|
||||
+ sget-object v0, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean; # always-true value
|
||||
+
|
||||
+ invoke-static {v0}, Lkotlinx/coroutines/flow/StateFlowKt;->MutableStateFlow(Ljava/lang/Object;)Lkotlinx/coroutines/flow/MutableStateFlow;
|
||||
+ 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
|
||||
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
|
||||
.locals 3
|
||||
|
||||
+ # rl-debug-unlock: force c() to always return true, bypassing the
|
||||
+ # applicationId-substring / in-app-bug-reports / export-logs flag checks.
|
||||
+ const/4 v0, 0x1
|
||||
+ return v0
|
||||
+ const/4 v0, 0x1 # force true
|
||||
+ return v0 # bypass debug flag
|
||||
+
|
||||
.line 1
|
||||
sget-object v0, Lh50/a;->a:Ljava/lang/String;
|
||||
|
||||
sget-object v0, Lj50/a;->a:Ljava/lang/String;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
--- a/ig/d.smali
|
||||
+++ b/ig/d.smali
|
||||
--- a/kg/e.smali
|
||||
+++ b/kg/e.smali
|
||||
@@ -26,8 +26,8 @@
|
||||
new-instance v0, Lig/d;
|
||||
new-instance v0, Lkg/e;
|
||||
|
||||
.line 2
|
||||
.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
|
||||
@@ -0,0 +1,436 @@
|
||||
.class public final Lradiant/MiniSeekerFloating;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.implements Lyl0/l;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static final INSTANCE:Lradiant/MiniSeekerFloating;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
new-instance v0, Lradiant/MiniSeekerFloating; # singleton
|
||||
|
||||
invoke-direct {v0}, Lradiant/MiniSeekerFloating;-><init>()V # ctor
|
||||
|
||||
sput-object v0, Lradiant/MiniSeekerFloating;->INSTANCE:Lradiant/MiniSeekerFloating; # publish
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public final invoke(Ljava/lang/Object;)Ljava/lang/Object;
|
||||
.locals 2
|
||||
|
||||
check-cast p1, Landroidx/compose/ui/graphics/drawscope/ContentDrawScope; # narrow type
|
||||
|
||||
invoke-interface {p1}, Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;->drawContent()V # draw pill content
|
||||
|
||||
invoke-static {}, Lradiant/MiniSeekerLine;->start()V # lazy subscribe
|
||||
|
||||
:try_start
|
||||
invoke-static {p1}, Lradiant/MiniSeekerFloating;->drawBorder(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V # draw progress
|
||||
:try_end
|
||||
.catch Ljava/lang/Throwable; {:try_start .. :try_end} :swallow
|
||||
|
||||
:swallow
|
||||
sget-object v0, Lkotlin/u;->a:Lkotlin/u; # Unit
|
||||
|
||||
return-object v0
|
||||
.end method
|
||||
|
||||
|
||||
.method private static drawBorder(Landroidx/compose/ui/graphics/drawscope/ContentDrawScope;)V
|
||||
.locals 60
|
||||
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->progressState:Landroidx/compose/runtime/MutableFloatState; # state
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F # read tracked
|
||||
|
||||
move-result v0 # raw progress
|
||||
|
||||
const/4 v1, 0x0 # lower bound
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->max(FF)F # clamp low
|
||||
|
||||
move-result v0 # clamped low
|
||||
|
||||
const/high16 v1, 0x3f800000 # 1.0f upper
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->min(FF)F # clamp high
|
||||
|
||||
move-result v0 # final progress
|
||||
|
||||
const/4 v1, 0x0 # zero
|
||||
|
||||
cmpg-float v2, v0, v1 # compare to zero
|
||||
|
||||
if-lez v2, :done # skip if empty
|
||||
|
||||
move/from16 v16, v0 # stash progress
|
||||
|
||||
invoke-interface/range {p0 .. p0}, Landroidx/compose/ui/graphics/drawscope/DrawScope;->getSize-NH-jbRc()J # size long
|
||||
|
||||
move-result-wide v0 # size pair
|
||||
|
||||
invoke-static {v0, v1}, Landroidx/compose/ui/geometry/Size;->getWidth-impl(J)F # extract width
|
||||
|
||||
move-result v2 # width px
|
||||
|
||||
move/from16 v17, v2 # stash width
|
||||
|
||||
invoke-static {v0, v1}, Landroidx/compose/ui/geometry/Size;->getHeight-impl(J)F # extract height
|
||||
|
||||
move-result v2 # height px
|
||||
|
||||
move/from16 v18, v2 # stash height
|
||||
|
||||
invoke-interface/range {p0 .. p0}, Landroidx/compose/ui/unit/Density;->getDensity()F # density
|
||||
|
||||
move-result v2 # px per dp
|
||||
|
||||
move/from16 v19, v2 # stash density
|
||||
|
||||
const/high16 v0, 0x42100000 # 36.0f dp
|
||||
|
||||
move/from16 v1, v19 # density
|
||||
|
||||
mul-float v2, v0, v1 # to px
|
||||
|
||||
move/from16 v20, v2 # stash r
|
||||
|
||||
const/high16 v0, 0x40000000 # 2.0f
|
||||
|
||||
move/from16 v1, v20 # r
|
||||
|
||||
mul-float v2, v1, v0 # 2*r
|
||||
|
||||
move/from16 v21, v2 # stash 2r
|
||||
|
||||
move/from16 v0, v17 # w
|
||||
|
||||
move/from16 v1, v20 # r
|
||||
|
||||
sub-float v2, v0, v1 # w-r
|
||||
|
||||
move/from16 v22, v2 # stash w-r
|
||||
|
||||
move/from16 v0, v18 # h
|
||||
|
||||
move/from16 v1, v20 # r
|
||||
|
||||
sub-float v2, v0, v1 # h-r
|
||||
|
||||
move/from16 v23, v2 # stash h-r
|
||||
|
||||
move/from16 v0, v17 # w
|
||||
|
||||
move/from16 v1, v21 # 2r
|
||||
|
||||
sub-float v2, v0, v1 # w-2r
|
||||
|
||||
move/from16 v24, v2 # stash w-2r
|
||||
|
||||
move/from16 v0, v18 # h
|
||||
|
||||
move/from16 v1, v21 # 2r
|
||||
|
||||
sub-float v2, v0, v1 # h-2r
|
||||
|
||||
move/from16 v25, v2 # stash h-2r
|
||||
|
||||
move/from16 v0, v17 # w
|
||||
|
||||
const/high16 v1, 0x40000000 # 2.0f
|
||||
|
||||
div-float v2, v0, v1 # w/2
|
||||
|
||||
move/from16 v26, v2 # stash top-center x
|
||||
|
||||
const/high16 v0, 0x42b40000 # 90.0f
|
||||
|
||||
move/from16 v27, v0 # stash 90
|
||||
|
||||
const/4 v0, 0x0 # zero
|
||||
|
||||
move/from16 v1, v27 # 90
|
||||
|
||||
sub-float v2, v0, v1 # -90
|
||||
|
||||
move/from16 v28, v2 # stash -90
|
||||
|
||||
const/high16 v0, 0x43340000 # 180.0f
|
||||
|
||||
move/from16 v29, v0 # stash 180
|
||||
|
||||
invoke-static {}, Landroidx/compose/ui/graphics/AndroidPath_androidKt;->Path()Landroidx/compose/ui/graphics/Path; # new path
|
||||
|
||||
move-result-object v0 # path
|
||||
|
||||
move-object/from16 v30, v0 # stash path
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v26 # w/2
|
||||
|
||||
const/4 v2, 0x0 # 0
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->moveTo(FF)V # start top-center
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v22 # w-r
|
||||
|
||||
const/4 v2, 0x0 # 0
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # top edge right
|
||||
|
||||
new-instance v3, Landroidx/compose/ui/geometry/Rect; # arc bounds
|
||||
|
||||
move/from16 v4, v24 # w-2r
|
||||
|
||||
const/4 v5, 0x0 # 0
|
||||
|
||||
move/from16 v6, v17 # w
|
||||
|
||||
move/from16 v7, v21 # 2r
|
||||
|
||||
invoke-direct {v3, v4, v5, v6, v7}, Landroidx/compose/ui/geometry/Rect;-><init>(FFFF)V # TR rect
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v4, v28 # -90
|
||||
|
||||
move/from16 v5, v27 # 90
|
||||
|
||||
const/4 v6, 0x0 # false
|
||||
|
||||
invoke-interface {v0, v3, v4, v5, v6}, Landroidx/compose/ui/graphics/Path;->arcTo(Landroidx/compose/ui/geometry/Rect;FFZ)V # TR corner
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v17 # w
|
||||
|
||||
move/from16 v2, v23 # h-r
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # right edge
|
||||
|
||||
new-instance v3, Landroidx/compose/ui/geometry/Rect; # arc bounds
|
||||
|
||||
move/from16 v4, v24 # w-2r
|
||||
|
||||
move/from16 v5, v25 # h-2r
|
||||
|
||||
move/from16 v6, v17 # w
|
||||
|
||||
move/from16 v7, v18 # h
|
||||
|
||||
invoke-direct {v3, v4, v5, v6, v7}, Landroidx/compose/ui/geometry/Rect;-><init>(FFFF)V # BR rect
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
const/4 v4, 0x0 # 0 deg
|
||||
|
||||
move/from16 v5, v27 # 90
|
||||
|
||||
const/4 v6, 0x0 # false
|
||||
|
||||
invoke-interface {v0, v3, v4, v5, v6}, Landroidx/compose/ui/graphics/Path;->arcTo(Landroidx/compose/ui/geometry/Rect;FFZ)V # BR corner
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v20 # r
|
||||
|
||||
move/from16 v2, v18 # h
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # bottom edge
|
||||
|
||||
new-instance v3, Landroidx/compose/ui/geometry/Rect; # arc bounds
|
||||
|
||||
const/4 v4, 0x0 # 0
|
||||
|
||||
move/from16 v5, v25 # h-2r
|
||||
|
||||
move/from16 v6, v21 # 2r
|
||||
|
||||
move/from16 v7, v18 # h
|
||||
|
||||
invoke-direct {v3, v4, v5, v6, v7}, Landroidx/compose/ui/geometry/Rect;-><init>(FFFF)V # BL rect
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v4, v27 # 90
|
||||
|
||||
move/from16 v5, v27 # 90
|
||||
|
||||
const/4 v6, 0x0 # false
|
||||
|
||||
invoke-interface {v0, v3, v4, v5, v6}, Landroidx/compose/ui/graphics/Path;->arcTo(Landroidx/compose/ui/geometry/Rect;FFZ)V # BL corner
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
const/4 v1, 0x0 # 0
|
||||
|
||||
move/from16 v2, v20 # r
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # left edge
|
||||
|
||||
new-instance v3, Landroidx/compose/ui/geometry/Rect; # arc bounds
|
||||
|
||||
const/4 v4, 0x0 # 0
|
||||
|
||||
const/4 v5, 0x0 # 0
|
||||
|
||||
move/from16 v6, v21 # 2r
|
||||
|
||||
move/from16 v7, v21 # 2r
|
||||
|
||||
invoke-direct {v3, v4, v5, v6, v7}, Landroidx/compose/ui/geometry/Rect;-><init>(FFFF)V # TL rect
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v4, v29 # 180
|
||||
|
||||
move/from16 v5, v27 # 90
|
||||
|
||||
const/4 v6, 0x0 # false
|
||||
|
||||
invoke-interface {v0, v3, v4, v5, v6}, Landroidx/compose/ui/graphics/Path;->arcTo(Landroidx/compose/ui/geometry/Rect;FFZ)V # TL corner
|
||||
|
||||
move-object/from16 v0, v30 # path
|
||||
|
||||
move/from16 v1, v26 # w/2
|
||||
|
||||
const/4 v2, 0x0 # 0
|
||||
|
||||
invoke-interface {v0, v1, v2}, Landroidx/compose/ui/graphics/Path;->lineTo(FF)V # close to start
|
||||
|
||||
const/high16 v0, 0x40000000 # 2.0f
|
||||
|
||||
move/from16 v1, v24 # w-2r
|
||||
|
||||
mul-float v2, v1, v0 # 2*(w-2r)
|
||||
|
||||
move/from16 v1, v25 # h-2r
|
||||
|
||||
mul-float v3, v1, v0 # 2*(h-2r)
|
||||
|
||||
add-float v4, v2, v3 # straight edges
|
||||
|
||||
const v5, 0x40c90fdb # 2*pi
|
||||
|
||||
move/from16 v6, v20 # r
|
||||
|
||||
mul-float v7, v6, v5 # arc length
|
||||
|
||||
add-float v8, v4, v7 # total perim
|
||||
|
||||
move/from16 v31, v8 # stash perim
|
||||
|
||||
move/from16 v0, v16 # progress
|
||||
|
||||
move/from16 v1, v31 # perim
|
||||
|
||||
mul-float v2, v0, v1 # visible length
|
||||
|
||||
move/from16 v32, v2 # stash visible
|
||||
|
||||
sget-object v0, Landroidx/compose/ui/graphics/PathEffect;->Companion:Landroidx/compose/ui/graphics/PathEffect$Companion; # companion
|
||||
|
||||
const/4 v1, 0x2 # length 2
|
||||
|
||||
new-array v1, v1, [F # intervals
|
||||
|
||||
const/4 v2, 0x0 # index 0
|
||||
|
||||
move/from16 v3, v32 # visible
|
||||
|
||||
aput v3, v1, v2 # dash length
|
||||
|
||||
const/4 v2, 0x1 # index 1
|
||||
|
||||
move/from16 v3, v31 # perim
|
||||
|
||||
aput v3, v1, v2 # gap length
|
||||
|
||||
const/4 v2, 0x0 # phase int
|
||||
|
||||
int-to-float v2, v2 # phase float
|
||||
|
||||
invoke-virtual {v0, v1, v2}, Landroidx/compose/ui/graphics/PathEffect$Companion;->dashPathEffect([FF)Landroidx/compose/ui/graphics/PathEffect; # dash effect
|
||||
|
||||
move-result-object v0 # effect
|
||||
|
||||
move-object/from16 v33, v0 # stash effect
|
||||
|
||||
const/high16 v0, 0x40000000 # 2.0f
|
||||
|
||||
move/from16 v1, v19 # density
|
||||
|
||||
mul-float v2, v0, v1 # stroke px
|
||||
|
||||
move/from16 v34, v2 # stash stroke width
|
||||
|
||||
new-instance v40, Landroidx/compose/ui/graphics/drawscope/Stroke; # stroke obj
|
||||
|
||||
move/from16 v41, v34 # width
|
||||
|
||||
const/16 v42, 0x0 # miter default
|
||||
|
||||
const/16 v43, 0x0 # cap default
|
||||
|
||||
const/16 v44, 0x0 # join default
|
||||
|
||||
move-object/from16 v45, v33 # effect
|
||||
|
||||
const/16 v46, 0xe # default mask
|
||||
|
||||
const/16 v47, 0x0 # marker null
|
||||
|
||||
invoke-direct/range {v40 .. v47}, Landroidx/compose/ui/graphics/drawscope/Stroke;-><init>(FFIILandroidx/compose/ui/graphics/PathEffect;ILkotlin/jvm/internal/DefaultConstructorMarker;)V # stroke ctor
|
||||
|
||||
move-object/from16 v35, v40 # stash stroke
|
||||
|
||||
const v0, -0x1 # white ARGB
|
||||
|
||||
invoke-static {v0}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack color
|
||||
|
||||
move-result-wide v0 # color long
|
||||
|
||||
move-wide/from16 v36, v0 # stash color
|
||||
|
||||
move-object/from16 v50, p0 # scope
|
||||
|
||||
move-object/from16 v51, v30 # path
|
||||
|
||||
move-wide/from16 v52, v36 # color
|
||||
|
||||
const/16 v54, 0x0 # alpha default
|
||||
|
||||
move-object/from16 v55, v35 # style
|
||||
|
||||
const/16 v56, 0x0 # filter null
|
||||
|
||||
const/16 v57, 0x0 # blend default
|
||||
|
||||
const/16 v58, 0x34 # default mask
|
||||
|
||||
const/16 v59, 0x0 # marker null
|
||||
|
||||
invoke-static/range {v50 .. v59}, Landroidx/compose/ui/graphics/drawscope/DrawScope;->drawPath-LG529CI$default(Landroidx/compose/ui/graphics/drawscope/DrawScope;Landroidx/compose/ui/graphics/Path;JFLandroidx/compose/ui/graphics/drawscope/DrawStyle;Landroidx/compose/ui/graphics/ColorFilter;IILjava/lang/Object;)V # draw stroke
|
||||
|
||||
:done
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,200 @@
|
||||
.class public final Lradiant/MiniSeekerLine;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static progressState:Landroidx/compose/runtime/MutableFloatState;
|
||||
|
||||
.field public static volatile vm:Ljh/h;
|
||||
|
||||
.field public static volatile sub:Lio/reactivex/disposables/Disposable;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0 # initial progress
|
||||
|
||||
invoke-static {v0}, Landroidx/compose/runtime/PrimitiveSnapshotStateKt;->mutableFloatStateOf(F)Landroidx/compose/runtime/MutableFloatState; # state holder
|
||||
|
||||
move-result-object v0 # state instance
|
||||
|
||||
sput-object v0, Lradiant/MiniSeekerLine;->progressState:Landroidx/compose/runtime/MutableFloatState; # publish state
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method private static dlog(Ljava/lang/String;)V
|
||||
.locals 1
|
||||
|
||||
const-string v0, "RLProgressLine" # log tag
|
||||
|
||||
invoke-static {v0, p0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I # info log
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public static start()V
|
||||
.locals 3
|
||||
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->sub:Lio/reactivex/disposables/Disposable; # current sub
|
||||
|
||||
if-eqz v0, :start_block # only run once
|
||||
|
||||
return-void
|
||||
|
||||
:start_block
|
||||
:try_start
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->vm:Ljh/h; # cached vm
|
||||
|
||||
if-nez v0, :got_vm # reuse if present
|
||||
|
||||
invoke-static {}, Lcom/aspiro/wamp/App$a;->a()Lcom/aspiro/wamp/App; # app singleton
|
||||
|
||||
move-result-object v0 # app
|
||||
|
||||
invoke-virtual {v0}, Lcom/aspiro/wamp/App;->e()Lf5/c; # dagger component
|
||||
|
||||
move-result-object v0 # component
|
||||
|
||||
check-cast v0, Lf5/d0$n2; # narrow type
|
||||
|
||||
iget-object v0, v0, Lf5/d0$n2;->z0:Ldagger/internal/j; # playqueue provider
|
||||
|
||||
invoke-interface {v0}, Lol0/a;->get()Ljava/lang/Object; # resolve
|
||||
|
||||
move-result-object v0 # playqueue
|
||||
|
||||
check-cast v0, Lcom/aspiro/wamp/playqueue/z0; # narrow type
|
||||
|
||||
new-instance v1, Ljh/h; # build vm
|
||||
|
||||
invoke-direct {v1, v0}, Ljh/h;-><init>(Lcom/aspiro/wamp/playqueue/z0;)V # ctor
|
||||
|
||||
sput-object v1, Lradiant/MiniSeekerLine;->vm:Ljh/h; # cache
|
||||
|
||||
:got_vm
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->vm:Ljh/h; # vm ref
|
||||
|
||||
invoke-virtual {v0}, Ljh/h;->a()V # activate emission
|
||||
|
||||
iget-object v0, v0, Ljh/h;->d:Lio/reactivex/subjects/BehaviorSubject; # position subject
|
||||
|
||||
invoke-static {}, Lio/reactivex/android/schedulers/AndroidSchedulers;->mainThread()Lio/reactivex/Scheduler; # main scheduler
|
||||
|
||||
move-result-object v1 # scheduler
|
||||
|
||||
invoke-virtual {v0, v1}, Lio/reactivex/Observable;->observeOn(Lio/reactivex/Scheduler;)Lio/reactivex/Observable; # observe main
|
||||
|
||||
move-result-object v0 # observable
|
||||
|
||||
new-instance v1, Lradiant/SeekerConsumer; # consumer
|
||||
|
||||
invoke-direct {v1}, Lradiant/SeekerConsumer;-><init>()V # ctor
|
||||
|
||||
invoke-virtual {v0, v1}, Lio/reactivex/Observable;->subscribe(Lio/reactivex/functions/Consumer;)Lio/reactivex/disposables/Disposable; # subscribe
|
||||
|
||||
move-result-object v0 # disposable
|
||||
|
||||
sput-object v0, Lradiant/MiniSeekerLine;->sub:Lio/reactivex/disposables/Disposable; # cache
|
||||
|
||||
const-string v0, "started" # log msg
|
||||
|
||||
invoke-static {v0}, Lradiant/MiniSeekerLine;->dlog(Ljava/lang/String;)V # info log
|
||||
|
||||
return-void
|
||||
:try_end
|
||||
.catch Ljava/lang/Throwable; {:try_start .. :try_end} :swallow
|
||||
|
||||
:swallow
|
||||
move-exception v0 # error
|
||||
|
||||
const-string v1, "start failed" # log msg
|
||||
|
||||
invoke-static {v1}, Lradiant/MiniSeekerLine;->dlog(Ljava/lang/String;)V # info log
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
.method public static render(Landroidx/compose/runtime/Composer;I)V
|
||||
.locals 5
|
||||
.annotation build Landroidx/compose/runtime/Composable;
|
||||
.end annotation
|
||||
|
||||
invoke-static {}, Lradiant/MiniSeekerLine;->start()V # lazy subscribe
|
||||
|
||||
sget-object v0, Lradiant/MiniSeekerLine;->progressState:Landroidx/compose/runtime/MutableFloatState; # state
|
||||
|
||||
invoke-interface {v0}, Landroidx/compose/runtime/MutableFloatState;->getFloatValue()F # read tracked
|
||||
|
||||
move-result v0 # raw progress
|
||||
|
||||
const/4 v1, 0x0 # lower bound
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->max(FF)F # clamp low
|
||||
|
||||
move-result v0 # clamped low
|
||||
|
||||
const/high16 v1, 0x3f800000 # 1.0f upper
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->min(FF)F # clamp high
|
||||
|
||||
move-result v0 # final progress
|
||||
|
||||
const v1, 0x52414c49 # 'RALI' slot key
|
||||
|
||||
invoke-interface {p0, v1}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open group
|
||||
|
||||
sget-object v1, Landroidx/compose/ui/Modifier;->Companion:Landroidx/compose/ui/Modifier$Companion; # base modifier
|
||||
|
||||
invoke-static {v1, v0}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxWidth(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier; # width fraction
|
||||
|
||||
move-result-object v0 # filled modifier
|
||||
|
||||
const/4 v1, 0x2 # 2 dp
|
||||
|
||||
int-to-float v1, v1 # to float
|
||||
|
||||
invoke-static {v1}, Landroidx/compose/ui/unit/Dp;->constructor-impl(F)F # box as Dp
|
||||
|
||||
move-result v1 # dp value
|
||||
|
||||
invoke-static {v0, v1}, Landroidx/compose/foundation/layout/SizeKt;->height-3ABfNKs(Landroidx/compose/ui/Modifier;F)Landroidx/compose/ui/Modifier; # 2dp tall
|
||||
|
||||
move-result-object v0 # sized modifier
|
||||
|
||||
const v1, -0x1 # 0xFFFFFFFF white
|
||||
|
||||
invoke-static {v1}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack color
|
||||
|
||||
move-result-wide v1 # color long
|
||||
|
||||
invoke-static {}, Landroidx/compose/ui/graphics/RectangleShapeKt;->getRectangleShape()Landroidx/compose/ui/graphics/Shape; # rect shape
|
||||
|
||||
move-result-object v3 # shape
|
||||
|
||||
invoke-static {v0, v1, v2, v3}, Landroidx/compose/foundation/BackgroundKt;->background-bw27NRU(Landroidx/compose/ui/Modifier;JLandroidx/compose/ui/graphics/Shape;)Landroidx/compose/ui/Modifier; # white bg
|
||||
|
||||
move-result-object v0 # final modifier
|
||||
|
||||
const/4 v1, 0x0 # composer $changed
|
||||
|
||||
invoke-static {v0, p0, v1}, Landroidx/compose/foundation/layout/SpacerKt;->Spacer(Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;I)V # draw line
|
||||
|
||||
invoke-interface {p0}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close group
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -1,6 +1,6 @@
|
||||
.class public final Lradiant/NoOp;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Ltl0/a;
|
||||
.implements Lyl0/a;
|
||||
|
||||
|
||||
# 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=rl-mobile"
|
||||
|
||||
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v2
|
||||
|
||||
new-instance v3, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v4, "https://api.atomix.one/rl-api"
|
||||
|
||||
invoke-direct {v3, v4}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v3, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
const/4 v4, 0x1
|
||||
|
||||
invoke-static {v3, v4}, Lradiant/RLAPILyricsWorker;->fetch(Ljava/lang/String;Z)Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
if-nez v3, :got_body
|
||||
|
||||
new-instance v3, Ljava/lang/StringBuilder;
|
||||
|
||||
const-string v4, "https://rl-api.kineticsand.net/lyrics"
|
||||
|
||||
invoke-direct {v3, v4}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||
|
||||
invoke-virtual {v3, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||
|
||||
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
const/4 v4, 0x0
|
||||
|
||||
invoke-static {v3, v4}, Lradiant/RLAPILyricsWorker;->fetch(Ljava/lang/String;Z)Ljava/lang/String;
|
||||
|
||||
move-result-object v3
|
||||
|
||||
if-nez v3, :got_body
|
||||
|
||||
const-string v4, "both primary and fallback fetches failed"
|
||||
|
||||
invoke-static {v4}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||
|
||||
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,60 @@
|
||||
.class public final Lradiant/SeekerConsumer;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
.implements Lio/reactivex/functions/Consumer;
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public final accept(Ljava/lang/Object;)V
|
||||
.locals 3
|
||||
|
||||
if-eqz p1, :ret # null guard
|
||||
|
||||
instance-of v0, p1, Ljh/b; # type guard
|
||||
|
||||
if-eqz v0, :ret # skip if wrong type
|
||||
|
||||
check-cast p1, Ljh/b; # narrow type
|
||||
|
||||
iget v0, p1, Ljh/b;->b:F # raw progress
|
||||
|
||||
invoke-static {v0}, Ljava/lang/Float;->isNaN(F)Z # nan check
|
||||
|
||||
move-result v1 # nan flag
|
||||
|
||||
if-eqz v1, :not_nan # skip clamp if finite
|
||||
|
||||
const/4 v0, 0x0 # default to zero
|
||||
|
||||
:not_nan
|
||||
const/4 v1, 0x0 # lower bound
|
||||
|
||||
invoke-static {v0, v1}, Ljava/lang/Math;->max(FF)F # clamp low
|
||||
|
||||
move-result v0 # clamped low
|
||||
|
||||
const/high16 v2, 0x3f800000 # 1.0f upper bound
|
||||
|
||||
invoke-static {v0, v2}, Ljava/lang/Math;->min(FF)F # clamp high
|
||||
|
||||
move-result v0 # final value
|
||||
|
||||
sget-object v1, Lradiant/MiniSeekerLine;->progressState:Landroidx/compose/runtime/MutableFloatState; # shared state
|
||||
|
||||
if-eqz v1, :ret # safety
|
||||
|
||||
invoke-interface {v1, v0}, Landroidx/compose/runtime/MutableFloatState;->setFloatValue(F)V # publish
|
||||
|
||||
:ret
|
||||
return-void
|
||||
.end method
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
|
||||
# direct methods
|
||||
.method public static final a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Ltl0/a;)V
|
||||
.method public static final a(ILandroidx/compose/runtime/Composer;Landroidx/compose/ui/Modifier;Lyl0/a;)V
|
||||
.locals 21
|
||||
.annotation build Landroidx/compose/runtime/Composable;
|
||||
.end annotation
|
||||
@@ -98,7 +98,7 @@
|
||||
|
||||
const/4 v4, 0x6
|
||||
|
||||
invoke-static {v2, v11, v4}, Lcom/squareup/ui/market/core/theme/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
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
|
||||
const/16 v18, 0x0
|
||||
|
||||
invoke-static/range {v15 .. v20}, Lcom/squareup/ui/market/components/MarketIconButtonKt;->P(Lcom/squareup/ui/market/core/theme/MarketStylesheet;Lcom/squareup/ui/market/core/components/properties/IconButton$Size;Lcom/squareup/ui/market/core/components/properties/IconButton$Rank;Lcom/squareup/ui/market/core/components/properties/IconButton$Variant;ILjava/lang/Object;)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
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
:cond_5
|
||||
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
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
.source "SourceFile"
|
||||
|
||||
# interfaces
|
||||
.implements Ltl0/p;
|
||||
.implements Lyl0/p;
|
||||
|
||||
|
||||
# virtual methods
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.class public final Lradiant/SpvFactory;
|
||||
.super Ljava/lang/Object;
|
||||
.implements Ltl0/l;
|
||||
.implements Lyl0/l;
|
||||
|
||||
|
||||
# static fields
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
.class public final Lradiant/StickyLyrics;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
|
||||
# static fields
|
||||
.field public static volatile enabled:Z
|
||||
|
||||
|
||||
# direct methods
|
||||
.method static constructor <clinit>()V
|
||||
.locals 1
|
||||
|
||||
const/4 v0, 0x0
|
||||
|
||||
sput-boolean v0, Lradiant/StickyLyrics;->enabled:Z
|
||||
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method private constructor <init>()V
|
||||
.locals 0
|
||||
|
||||
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||
|
||||
return-void
|
||||
.end method
|
||||
@@ -0,0 +1,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;
|
||||
@@ -1,26 +1,26 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/g0.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/g0.smali
|
||||
@@ -666,8 +666,23 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/h0.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/h0.smali
|
||||
@@ -669,8 +669,23 @@
|
||||
move-object v5, v1
|
||||
|
||||
.line 288
|
||||
.line 289
|
||||
+ const v1, 0x52414443 # group key
|
||||
+
|
||||
+ invoke-interface {v7, v1}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open group around the cover
|
||||
+
|
||||
+ iget-object v1, v0, Lcom/tidal/android/feature/playerscreen/ui/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
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
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
|
||||
+
|
||||
.line 289
|
||||
.line 290
|
||||
.line 291
|
||||
.line 292
|
||||
|
||||
@@ -1,11 +1,37 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/composables/k1.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/composables/k1.smali
|
||||
@@ -64,7 +64,7 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/composables/o1.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/composables/o1.smali
|
||||
@@ -64,26 +64,29 @@
|
||||
|
||||
.line 16
|
||||
.line 17
|
||||
- iget v2, p0, Lcom/tidal/android/feature/playerscreen/ui/composables/k1;->a:F
|
||||
+ const/high16 v2, 0x43480000 # hardcode top fade region to 200dp (decouple from contentPadding)
|
||||
- iget v2, p0, Lcom/tidal/android/feature/playerscreen/ui/composables/o1;->a:F
|
||||
+ const/high16 v2, 0x43200000 # 160f start dp
|
||||
|
||||
.line 18
|
||||
.line 19
|
||||
invoke-interface {p1, v2}, Landroidx/compose/ui/unit/Density;->toPx-0680j_4(F)F
|
||||
|
||||
.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
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/b0.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/b0.smali
|
||||
@@ -45,7 +45,7 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/c0.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/c0.smali
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
# virtual methods
|
||||
.method public final invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
.line 1
|
||||
move-object/from16 v0, p0
|
||||
@@ -460,6 +460,64 @@
|
||||
@@ -461,6 +461,64 @@
|
||||
.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
|
||||
+
|
||||
@@ -29,7 +29,7 @@
|
||||
+
|
||||
+ const/16 v24, 0x0 # synthetic null
|
||||
+
|
||||
+ invoke-static/range {v18 .. v24}, Landroidx/compose/foundation/ClickableKt;->clickable-XHw0xAI$default(Landroidx/compose/ui/Modifier;ZLjava/lang/String;Landroidx/compose/ui/semantics/Role;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
|
||||
+
|
||||
@@ -69,7 +69,7 @@
|
||||
+
|
||||
+ 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 202
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# rl-locals: com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali e( 79
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
|
||||
@@ -4931,7 +4931,11 @@
|
||||
const/4 v10, 0x0
|
||||
@@ -4945,7 +4945,11 @@
|
||||
const/4 v7, 0x0
|
||||
|
||||
.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
|
||||
+
|
||||
+ invoke-interface {v2, v10}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty
|
||||
@@ -14,21 +14,21 @@
|
||||
|
||||
.line 227
|
||||
invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V
|
||||
@@ -5838,6 +5838,22 @@
|
||||
@@ -5720,6 +5724,22 @@
|
||||
:cond_51
|
||||
check-cast v4, Ltl0/a;
|
||||
check-cast v4, Lyl0/a;
|
||||
|
||||
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
|
||||
+
|
||||
@@ -36,4 +36,4 @@
|
||||
+
|
||||
+ 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
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
--- a/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
|
||||
|
||||
.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
|
||||
+
|
||||
+ invoke-interface {v2, v9}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open empty
|
||||
@@ -13,39 +13,38 @@
|
||||
|
||||
.line 248
|
||||
invoke-interface {v2}, Landroidx/compose/runtime/Composer;->endNode()V
|
||||
@@ -5758,17 +5758,19 @@
|
||||
@@ -5770,16 +5774,20 @@
|
||||
.line 335
|
||||
: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, v6}, Landroidx/compose/foundation/text/input/internal/selection/l;-><init>(Ljava/lang/Object;I)V
|
||||
- invoke-direct {v4, v11, v1}, Lcom/aspiro/wamp/tidalconnect/playback/i;-><init>(Ljava/lang/Object;I)V
|
||||
-
|
||||
- .line 336
|
||||
- invoke-interface {v7, v4}, Landroidx/compose/runtime/Composer;->updateRememberedValue(Ljava/lang/Object;)V
|
||||
-
|
||||
- .line 337
|
||||
- :cond_54
|
||||
- check-cast v4, Ltl0/a;
|
||||
- check-cast v4, Lyl0/a;
|
||||
-
|
||||
- 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
|
||||
+ new-instance v4, Lcom/tidal/android/feature/playerscreen/ui/f0; # connect click lambda
|
||||
- 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, Landroidx/navigation/fragment/k; # connect click lambda factory
|
||||
+
|
||||
+ 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}, Landroidx/navigation/fragment/k;-><init>(Ljava/lang/Object;I)V # build lambda
|
||||
+
|
||||
+ invoke-interface {v7, v4}, Landroidx/compose/runtime/Composer;->updateRememberedValue(Ljava/lang/Object;)V # cache lambda
|
||||
+
|
||||
+ :cond_54
|
||||
+ check-cast v4, Ltl0/a;
|
||||
+ :cond_54 # cache join
|
||||
+ check-cast v4, Lyl0/a; # cast to Function0
|
||||
+
|
||||
+ iget-object v8, v10, Lcom/tidal/android/feature/playerscreen/ui/r$a;->d:Lcom/tidal/android/feature/playerscreen/ui/b; # broadcast state
|
||||
+
|
||||
+ const/4 v2, 0x0 # changed flags
|
||||
+
|
||||
+ invoke-static {v8, v4, v13, v7, v2}, Lcom/tidal/android/feature/playerscreen/ui/composables/BroadcastButtonKt;->b(Lcom/tidal/android/feature/playerscreen/ui/b;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
|
||||
|
||||
@@ -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
|
||||
@@ -1,7 +1,7 @@
|
||||
--- a/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
|
||||
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali
|
||||
@@ -5854,17 +5854,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
|
||||
@@ -5725,17 +5725,38 @@
|
||||
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
|
||||
+
|
||||
@@ -11,13 +11,13 @@
|
||||
+
|
||||
+ 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
|
||||
|
||||
@@ -25,17 +25,17 @@
|
||||
|
||||
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
|
||||
+
|
||||
+ 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)
|
||||
+
|
||||
+ invoke-interface {v7, v2}, Landroidx/compose/runtime/Composer;->startReplaceGroup(I)V # open 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;
|
||||
@@ -0,0 +1,69 @@
|
||||
--- a/com/tidal/android/feature/appscaffold/ui/composable/a.smali
|
||||
+++ b/com/tidal/android/feature/appscaffold/ui/composable/a.smali
|
||||
@@ -116,7 +116,7 @@
|
||||
|
||||
.line 46
|
||||
.line 47
|
||||
- const/16 v0, 0x24
|
||||
+ const/16 v0, 0x0 # zero pill corner radius
|
||||
|
||||
.line 48
|
||||
.line 49
|
||||
@@ -162,7 +162,7 @@
|
||||
|
||||
.line 68
|
||||
.line 69
|
||||
- const/16 v0, 0x8
|
||||
+ const/16 v0, 0x0 # zero bottom padding
|
||||
|
||||
.line 70
|
||||
.line 71
|
||||
--- a/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||
+++ b/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||
@@ -2756,12 +2756,7 @@
|
||||
invoke-static {v0, v8, v11, v12}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxWidth$default(Landroidx/compose/ui/Modifier;FILjava/lang/Object;)Landroidx/compose/ui/Modifier;
|
||||
|
||||
move-result-object v0
|
||||
-
|
||||
- .line 140
|
||||
- invoke-static {v0}, Landroidx/compose/foundation/layout/WindowInsetsPadding_androidKt;->navigationBarsPadding(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
|
||||
-
|
||||
- move-result-object v0
|
||||
-
|
||||
+ # flush bottom; skip nav inset
|
||||
.line 141
|
||||
invoke-interface/range {v17 .. v17}, Landroidx/compose/runtime/State;->getValue()Ljava/lang/Object;
|
||||
|
||||
@@ -2773,6 +2768,8 @@
|
||||
|
||||
move-result v10
|
||||
|
||||
+ const/4 v10, 0x0 # zero horizontal padding
|
||||
+
|
||||
const/4 v11, 0x2
|
||||
|
||||
.line 142
|
||||
@@ -2816,6 +2813,12 @@
|
||||
|
||||
move-result-wide v10
|
||||
|
||||
+ const v8, -0x1000000 # 0xFF000000 opaque black
|
||||
+
|
||||
+ invoke-static {v8}, Landroidx/compose/ui/graphics/ColorKt;->Color(I)J # pack to color long
|
||||
+
|
||||
+ move-result-wide v10 # override theme color
|
||||
+
|
||||
.line 147
|
||||
sget-object v8, Lcom/tidal/android/feature/appscaffold/ui/composable/a;->c:Landroidx/compose/foundation/shape/RoundedCornerShape;
|
||||
|
||||
@@ -2969,6 +2972,10 @@
|
||||
|
||||
invoke-static {v12, v0, v8}, Landroidx/compose/runtime/Updater;->set-impl(Landroidx/compose/runtime/Composer;Ljava/lang/Object;Lyl0/p;)V
|
||||
|
||||
+ const/4 v8, 0x0 # composer $changed flags
|
||||
+
|
||||
+ invoke-static {v15, v8}, Lradiant/MiniSeekerLine;->render(Landroidx/compose/runtime/Composer;I)V # draw top-edge seeker
|
||||
+
|
||||
.line 172
|
||||
sget-object v0, Landroidx/compose/foundation/layout/ColumnScopeInstance;->INSTANCE:Landroidx/compose/foundation/layout/ColumnScopeInstance;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||
+++ b/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||
@@ -2829,6 +2829,12 @@
|
||||
|
||||
move-result-object v0
|
||||
|
||||
+ sget-object v8, Lradiant/MiniSeekerFloating;->INSTANCE:Lradiant/MiniSeekerFloating; # draw lambda
|
||||
+
|
||||
+ invoke-static {v0, v8}, Landroidx/compose/ui/draw/DrawModifierKt;->drawWithContent(Landroidx/compose/ui/Modifier;Lyl0/l;)Landroidx/compose/ui/Modifier; # overlay perimeter stroke
|
||||
+
|
||||
+ move-result-object v0 # updated modifier
|
||||
+
|
||||
.line 150
|
||||
sget-object v8, Lkotlin/u;->a:Lkotlin/u;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
--- a/com/tidal/android/feature/appscaffold/ui/composable/a.smali
|
||||
+++ b/com/tidal/android/feature/appscaffold/ui/composable/a.smali
|
||||
@@ -116,7 +116,7 @@
|
||||
|
||||
.line 46
|
||||
.line 47
|
||||
- const/16 v0, 0x24
|
||||
+ const/16 v0, 0x0 # zero pill corner radius
|
||||
|
||||
.line 48
|
||||
.line 49
|
||||
@@ -162,7 +162,7 @@
|
||||
|
||||
.line 68
|
||||
.line 69
|
||||
- const/16 v0, 0x8
|
||||
+ const/16 v0, 0x0 # zero bottom padding
|
||||
|
||||
.line 70
|
||||
.line 71
|
||||
--- a/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||
+++ b/com/tidal/android/feature/appscaffold/ui/composable/i.smali
|
||||
@@ -2756,12 +2756,7 @@
|
||||
invoke-static {v0, v8, v11, v12}, Landroidx/compose/foundation/layout/SizeKt;->fillMaxWidth$default(Landroidx/compose/ui/Modifier;FILjava/lang/Object;)Landroidx/compose/ui/Modifier;
|
||||
|
||||
move-result-object v0
|
||||
-
|
||||
- .line 140
|
||||
- invoke-static {v0}, Landroidx/compose/foundation/layout/WindowInsetsPadding_androidKt;->navigationBarsPadding(Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
|
||||
-
|
||||
- move-result-object v0
|
||||
-
|
||||
+ # flush bottom; skip nav inset
|
||||
.line 141
|
||||
invoke-interface/range {v17 .. v17}, Landroidx/compose/runtime/State;->getValue()Ljava/lang/Object;
|
||||
|
||||
@@ -2773,6 +2768,8 @@
|
||||
|
||||
move-result v10
|
||||
|
||||
+ const/4 v10, 0x0 # zero horizontal padding
|
||||
+
|
||||
const/4 v11, 0x2
|
||||
|
||||
.line 142
|
||||
@@ -2969,6 +2966,10 @@
|
||||
|
||||
invoke-static {v12, v0, v8}, Landroidx/compose/runtime/Updater;->set-impl(Landroidx/compose/runtime/Composer;Ljava/lang/Object;Lyl0/p;)V
|
||||
|
||||
+ const/4 v8, 0x0 # composer $changed flags
|
||||
+
|
||||
+ invoke-static {v15, v8}, Lradiant/MiniSeekerLine;->render(Landroidx/compose/runtime/Composer;I)V # draw top-edge seeker
|
||||
+
|
||||
.line 172
|
||||
sget-object v0, Landroidx/compose/foundation/layout/ColumnScopeInstance;->INSTANCE:Landroidx/compose/foundation/layout/ColumnScopeInstance;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# rl-locals: com/tidal/android/feature/playerscreen/ui/PlayerScreenKt.smali e( 71
|
||||
--- a/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
|
||||
+
|
||||
@@ -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 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
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
+ 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-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
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
+ move-result-object v7
|
||||
+ move-result-object v7 # crop scale
|
||||
+
|
||||
+ move-object/from16 v61, v6 # request
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
@@ -89,47 +89,47 @@
|
||||
+
|
||||
+ move-object/from16 v66, v4 # cover uuid
|
||||
+
|
||||
+ const/16 v67, 0x0
|
||||
+ const/16 v67, 0x0 # null onError
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
+ const/4 v4, 0x0 # fraction unused
|
||||
+
|
||||
+ const/4 v5, 0x1 # $default mask
|
||||
+ const/4 v5, 0x1 # default mask
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
+ 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
|
||||
+
|
||||
+ :radiant_skip
|
||||
+ :radiant_skip # skip target
|
||||
+ invoke-interface {v10}, Landroidx/compose/runtime/Composer;->endReplaceGroup()V # close group
|
||||
+
|
||||
.line 138
|
||||
|
||||
Reference in New Issue
Block a user