From 193d5ad90da8aff7fcb0d799b404f359b1ee4714 Mon Sep 17 00:00:00 2001 From: meowarex Date: Mon, 25 May 2026 21:13:00 +1000 Subject: [PATCH] Overhaul Manager Updates & Logging --- .../meowarex/rlmobile/ManagerApplication.kt | 2 - .../rlmobile/manager/InstallLogManager.kt | 65 +++------- .../dialogs/DeleteLogsDialogPreview.kt | 19 --- .../logs/LogsListScreenLoadedPreview.kt | 78 ----------- .../screens/logs/LogsListScreenNonePreview.kt | 20 --- .../rlmobile/ui/screens/home/HomeScreen.kt | 27 ++-- .../ui/screens/logs/LogsListScreen.kt | 99 -------------- .../ui/screens/logs/LogsListScreenModel.kt | 71 ---------- .../screens/logs/components/LogEntryCard.kt | 122 ------------------ .../screens/logs/components/LogsListAppBar.kt | 37 ------ .../ui/screens/logs/components/LogsNone.kt | 33 ----- .../components/dialogs/DeleteLogsDialog.kt | 53 -------- .../ui/widgets/updater/UpdaterViewModel.kt | 4 + .../app/src/main/res/drawable/ic_receipt.xml | 9 -- .../src/main/res/drawable/ic_reciept_off.xml | 9 -- .../app/src/main/res/drawable/ic_update.xml | 2 +- Manager/app/src/main/res/values/strings.xml | 8 -- 17 files changed, 40 insertions(+), 618 deletions(-) delete mode 100644 Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/dialogs/DeleteLogsDialogPreview.kt delete mode 100644 Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/screens/logs/LogsListScreenLoadedPreview.kt delete mode 100644 Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/screens/logs/LogsListScreenNonePreview.kt delete mode 100644 Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/LogsListScreen.kt delete mode 100644 Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/LogsListScreenModel.kt delete mode 100644 Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogEntryCard.kt delete mode 100644 Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogsListAppBar.kt delete mode 100644 Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogsNone.kt delete mode 100644 Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/dialogs/DeleteLogsDialog.kt delete mode 100644 Manager/app/src/main/res/drawable/ic_receipt.xml delete mode 100644 Manager/app/src/main/res/drawable/ic_reciept_off.xml diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ManagerApplication.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ManagerApplication.kt index b37863e..179c417 100644 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ManagerApplication.kt +++ b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ManagerApplication.kt @@ -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) }) diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/manager/InstallLogManager.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/manager/InstallLogManager.kt index 7d24747..165d42c 100644 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/manager/InstallLogManager.kt +++ b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/manager/InstallLogManager.kt @@ -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() - /** - * 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 { - 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 diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/dialogs/DeleteLogsDialogPreview.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/dialogs/DeleteLogsDialogPreview.kt deleted file mode 100644 index 658f574..0000000 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/dialogs/DeleteLogsDialogPreview.kt +++ /dev/null @@ -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 = {}, - ) - } -} diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/screens/logs/LogsListScreenLoadedPreview.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/screens/logs/LogsListScreenLoadedPreview.kt deleted file mode 100644 index f86751c..0000000 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/screens/logs/LogsListScreenLoadedPreview.kt +++ /dev/null @@ -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 = 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, - ), -) diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/screens/logs/LogsListScreenNonePreview.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/screens/logs/LogsListScreenNonePreview.kt deleted file mode 100644 index da80c69..0000000 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/previews/screens/logs/LogsListScreenNonePreview.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.meowarex.rlmobile.ui.previews.screens.logs - -import android.content.res.Configuration -import androidx.compose.runtime.* -import androidx.compose.ui.tooling.preview.Preview -import com.meowarex.rlmobile.ui.screens.logs.LogsScreenContent -import com.meowarex.rlmobile.ui.theme.ManagerTheme - -@Composable -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO) -private fun LogsListScreenNonePreview() { - ManagerTheme { - LogsScreenContent( - logs = remember { mutableStateListOf() }, - onOpenLog = {}, - onDeleteLogs = {}, - ) - } -} diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/home/HomeScreen.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/home/HomeScreen.kt index 45c2295..4a2aec5 100644 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/home/HomeScreen.kt +++ b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/home/HomeScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.activity.ComponentActivity import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -32,13 +33,14 @@ import com.meowarex.rlmobile.ui.components.SegmentedButton import com.meowarex.rlmobile.ui.components.Tag import com.meowarex.rlmobile.ui.screens.about.AboutScreen import com.meowarex.rlmobile.ui.screens.home.components.CommitList -import com.meowarex.rlmobile.ui.screens.logs.LogsListScreen import com.meowarex.rlmobile.ui.screens.patchopts.PatchOptionsScreen import com.meowarex.rlmobile.ui.screens.settings.SettingsScreen import com.meowarex.rlmobile.ui.widgets.managerupdate.ManagerUpdateDialog +import com.meowarex.rlmobile.ui.widgets.updater.UpdaterViewModel import com.meowarex.rlmobile.util.* import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize +import org.koin.androidx.compose.koinViewModel @Parcelize class HomeScreen : Screen, Parcelable { @@ -50,6 +52,9 @@ class HomeScreen : Screen, Parcelable { val navigator = LocalNavigator.currentOrThrow val scope = rememberCoroutineScope() val model = koinScreenModel() + val activity = LocalContext.current as ComponentActivity + val updater = koinViewModel(viewModelStoreOwner = activity) + val managerUpdateAvailable = updater.targetVersion != null LifecycleResumeEffect(Unit) { model.refresh(delay = true) @@ -67,18 +72,20 @@ class HomeScreen : Screen, Parcelable { contentDescription = stringResource(R.string.navigation_refresh), ) } + if (managerUpdateAvailable) { + IconButton(onClick = updater::reopenDialog) { + Icon( + painterResource(R.drawable.ic_update), + contentDescription = stringResource(R.string.action_update), + ) + } + } IconButton(onClick = { navigator.push(AboutScreen()) }) { Icon( painterResource(R.drawable.ic_info), contentDescription = stringResource(R.string.navigation_about), ) } - IconButton(onClick = { navigator.push(LogsListScreen()) }) { - Icon( - painterResource(R.drawable.ic_receipt), - contentDescription = stringResource(R.string.navigation_logs), - ) - } IconButton(onClick = { navigator.push(SettingsScreen()) }) { Icon( painterResource(R.drawable.ic_settings), @@ -107,6 +114,7 @@ class HomeScreen : Screen, Parcelable { is HomeState.Loaded -> HomeContent( state = state, commits = model.commits, + managerUpdateAvailable = managerUpdateAvailable, onInstall = { navigator.pushOnce(PatchOptionsScreen()) }, onRepatch = { scope.launchIO { @@ -134,6 +142,7 @@ class HomeScreen : Screen, Parcelable { private fun ColumnScope.HomeContent( state: HomeState.Loaded, commits: kotlinx.coroutines.flow.Flow>, + managerUpdateAvailable: Boolean, onInstall: () -> Unit, onRepatch: () -> Unit, onLaunch: () -> Unit, @@ -200,12 +209,14 @@ private fun ColumnScope.HomeContent( } } + val blockedByManagerUpdate = managerUpdateAvailable && (patchesBehind || tidalBehind) Button( onClick = if (install == null) onInstall else onRepatch, - enabled = state.latestTidalVersionCode != null, + enabled = state.latestTidalVersionCode != null && !blockedByManagerUpdate, modifier = Modifier.fillMaxWidth(), ) { val label = when { + blockedByManagerUpdate -> "Manager Update Required" state.latestTidalVersionCode == null -> "Loading…" install == null -> "Install" patchesBehind && tidalBehind -> "Update Patches & TIDAL" diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/LogsListScreen.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/LogsListScreen.kt deleted file mode 100644 index c5347e0..0000000 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/LogsListScreen.kt +++ /dev/null @@ -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() - - 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, - 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(), - ) - } - } - } -} diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/LogsListScreenModel.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/LogsListScreenModel.kt deleted file mode 100644 index 567517a..0000000 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/LogsListScreenModel.kt +++ /dev/null @@ -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() - - 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?, -) diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogEntryCard.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogEntryCard.kt deleted file mode 100644 index edfb1b8..0000000 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogEntryCard.kt +++ /dev/null @@ -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, - ) - } - } - } - } -} diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogsListAppBar.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogsListAppBar.kt deleted file mode 100644 index ec86ab4..0000000 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogsListAppBar.kt +++ /dev/null @@ -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) - ) - } - } - ) -} diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogsNone.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogsNone.kt deleted file mode 100644 index 0b64045..0000000 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/LogsNone.kt +++ /dev/null @@ -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), - ) - } - } -} diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/dialogs/DeleteLogsDialog.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/dialogs/DeleteLogsDialog.kt deleted file mode 100644 index 14b6b13..0000000 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/screens/logs/components/dialogs/DeleteLogsDialog.kt +++ /dev/null @@ -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)) - } - } - ) -} diff --git a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/widgets/updater/UpdaterViewModel.kt b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/widgets/updater/UpdaterViewModel.kt index 9b2089f..7368982 100644 --- a/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/widgets/updater/UpdaterViewModel.kt +++ b/Manager/app/src/main/kotlin/com/meowarex/rlmobile/ui/widgets/updater/UpdaterViewModel.kt @@ -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 diff --git a/Manager/app/src/main/res/drawable/ic_receipt.xml b/Manager/app/src/main/res/drawable/ic_receipt.xml deleted file mode 100644 index 0b9203d..0000000 --- a/Manager/app/src/main/res/drawable/ic_receipt.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/Manager/app/src/main/res/drawable/ic_reciept_off.xml b/Manager/app/src/main/res/drawable/ic_reciept_off.xml deleted file mode 100644 index e7c6f35..0000000 --- a/Manager/app/src/main/res/drawable/ic_reciept_off.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/Manager/app/src/main/res/drawable/ic_update.xml b/Manager/app/src/main/res/drawable/ic_update.xml index 297f620..d682d6e 100644 --- a/Manager/app/src/main/res/drawable/ic_update.xml +++ b/Manager/app/src/main/res/drawable/ic_update.xml @@ -5,5 +5,5 @@ android:viewportHeight="960"> + 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" /> diff --git a/Manager/app/src/main/res/values/strings.xml b/Manager/app/src/main/res/values/strings.xml index 2a21323..07d2f44 100644 --- a/Manager/app/src/main/res/values/strings.xml +++ b/Manager/app/src/main/res/values/strings.xml @@ -129,7 +129,6 @@ Back About - Logs Settings Refresh @@ -303,13 +302,6 @@ Export APK Share log - Logs - No logs present! - Delete all logs - Successfully deleted all logs - Clear Logs - Are you sure you want to permanently delete all the logs? - Active installation Progress notifications sent during a minimized installation Radiant Lyrics installation ready!