Code Review for UI Rewrite

This commit is contained in:
2026-05-21 00:38:27 +10:00
parent 630154ed78
commit c95bb0633f
10 changed files with 66 additions and 26 deletions
@@ -26,6 +26,7 @@ import com.meowarex.rlmobile.ui.screens.settings.SettingsModel
import com.meowarex.rlmobile.ui.widgets.updater.UpdaterViewModel
import com.meowarex.rlmobile.updatechecker.UpdateCheckWorker
import kotlinx.coroutines.Dispatchers
import org.koin.android.ext.android.get
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
import org.koin.core.module.dsl.*
@@ -101,7 +102,12 @@ class ManagerApplication : Application() {
.build()
}
// Schedule periodic update check
UpdateCheckWorker.schedule(this)
// Schedule periodic update check only when the user has opted in,
// so the disabled state survives app restarts instead of being re-enqueued.
if (get<PreferencesManager>().autoUpdateCheck) {
UpdateCheckWorker.schedule(this)
} else {
UpdateCheckWorker.cancel(this)
}
}
}
@@ -10,7 +10,10 @@ class CommitsPagingSource(
) : PagingSource<Int, GithubCommit>() {
override fun getRefreshKey(state: PagingState<Int, GithubCommit>): Int? =
state.anchorPosition?.let { state.closestPageToPosition(it)?.prevKey }
state.anchorPosition?.let {
val page = state.closestPageToPosition(it) ?: return null
page.prevKey?.plus(1) ?: page.nextKey?.minus(1)
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, GithubCommit> {
val page = params.key ?: 0
@@ -51,8 +51,16 @@ class SmaliPatchStep : Step(), IDexProvider, KoinComponent {
if (patchFile.endsWith(".smali") && patchFile.startsWith("extension/")) {
val relative = patchFile.removePrefix("extension/")
val out = smaliDir.resolve(relative)
out.parentFile?.mkdirs()
out.writeBytes(zip.openEntry(patchFile)!!.read())
// Guard against zip-slip: a crafted entry could otherwise escape smaliDir.
val baseCanonical = smaliDir.canonicalPath + File.separator
val outCanonical = out.canonicalPath
if (!outCanonical.startsWith(baseCanonical)) {
throw SecurityException("Zip entry escapes target directory: $patchFile")
}
val entry = zip.openEntry(patchFile)
?: throw FileNotFoundException("Missing zip entry: $patchFile")
out.canonicalFile.parentFile?.mkdirs()
out.writeBytes(entry.read())
container.log("Extracted extension smali: $relative")
continue
}
@@ -35,7 +35,8 @@ object ManifestPatcher {
}
}
})
val origPkg = originalPackage ?: "com.aspiro.tidal"
val origPkg = originalPackage
?: throw IllegalStateException("Manifest has no package attribute; refusing to rewrite authorities/permissions without a known originalPackage")
val reader = AxmlReader(manifestBytes)
val writer = AxmlWriter()
@@ -104,7 +105,7 @@ object ManifestPatcher {
super.attr(
ns, name, resourceId, type,
when (name) {
"name" -> (value as String).replace(origPkg, packageName)
"name" -> (value as? String)?.replace(origPkg, packageName) ?: value
else -> value
}
)
@@ -155,7 +156,7 @@ object ManifestPatcher {
super.attr(
ns, name, resourceId, type,
if (name == "authorities") {
(value as String).replace(origPkg, packageName)
(value as? String)?.replace(origPkg, packageName) ?: value
} else {
value
}
@@ -97,6 +97,7 @@ class HomeModel(
fun openApp(packageName: String) {
val launchIntent = application.packageManager.getLaunchIntentForPackage(packageName)
if (launchIntent != null) {
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
application.startActivity(launchIntent)
} else {
application.showToast(R.string.launch_app_fail)
@@ -12,10 +12,15 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
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.compose.ui.platform.LocalContext
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.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.paging.compose.collectAsLazyPagingItems
import cafe.adriel.voyager.core.screen.Screen
@@ -55,16 +60,28 @@ class HomeScreen : Screen, Parcelable {
title = { Text(stringResource(R.string.navigation_home)) },
actions = {
IconButton(onClick = { model.refresh() }) {
Icon(painterResource(R.drawable.ic_refresh), contentDescription = null)
Icon(
painterResource(R.drawable.ic_refresh),
contentDescription = stringResource(R.string.navigation_refresh),
)
}
IconButton(onClick = { navigator.push(AboutScreen()) }) {
Icon(painterResource(R.drawable.ic_info), contentDescription = null)
Icon(
painterResource(R.drawable.ic_info),
contentDescription = stringResource(R.string.navigation_about),
)
}
IconButton(onClick = { navigator.push(LogsListScreen()) }) {
Icon(painterResource(R.drawable.ic_receipt), contentDescription = null)
Icon(
painterResource(R.drawable.ic_receipt),
contentDescription = stringResource(R.string.navigation_logs),
)
}
IconButton(onClick = { navigator.push(SettingsScreen()) }) {
Icon(painterResource(R.drawable.ic_settings), contentDescription = null)
Icon(
painterResource(R.drawable.ic_settings),
contentDescription = stringResource(R.string.navigation_settings),
)
}
},
)
@@ -117,17 +134,19 @@ private fun ColumnScope.HomeContent(
val currentVersionName = install?.version?.let { "v${it.toString()}" }
val latestVersionName = state.latestTidalVersionCode?.let { "build $it" }
if (install?.icon != null) {
val fallbackPainter = if (install?.icon == null) {
// R.mipmap.ic_launcher is an adaptive-icon XML on API 26+, which painterResource cannot decode.
val context = LocalContext.current
remember {
val drawable = ContextCompat.getDrawable(context, R.mipmap.ic_launcher)
drawable?.toBitmap()?.asImageBitmap()?.let(::BitmapPainter)
}
} else null
val iconPainter = install?.icon ?: fallbackPainter
if (iconPainter != null) {
Image(
painter = install.icon,
contentDescription = null,
modifier = Modifier
.size(60.dp)
.clip(CircleShape),
)
} else {
Image(
painter = painterResource(R.mipmap.ic_launcher),
painter = iconPainter,
contentDescription = null,
modifier = Modifier
.size(60.dp)
@@ -38,7 +38,7 @@ data class PatchOptions(
companion object {
val Default = PatchOptions(
appName = "TIDAL",
packageName = "com.tidal.music",
packageName = "com.aspiro.tidal",
debuggable = false,
customInjector = null,
customPatches = null,
@@ -7,6 +7,7 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.util.Log
import androidx.core.app.ActivityCompat
@@ -14,7 +15,6 @@ import androidx.core.app.NotificationCompat
import androidx.core.content.getSystemService
import androidx.work.*
import com.meowarex.rlmobile.BuildConfig
import com.meowarex.rlmobile.MainActivity
import com.meowarex.rlmobile.R
import com.meowarex.rlmobile.manager.PreferencesManager
import com.meowarex.rlmobile.network.services.RadiantLyricsGithubService
@@ -81,7 +81,7 @@ class UpdateCheckWorker(
val pendingIntent = PendingIntent.getActivity(
applicationContext,
0,
Intent(applicationContext, MainActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) },
Intent(Intent.ACTION_VIEW, Uri.parse(releaseUrl)).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) },
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
)