feat: logs list screen
This commit is contained in:
parent
3107a68986
commit
269fd531b3
17 changed files with 496 additions and 30 deletions
|
|
@ -11,6 +11,7 @@ import com.aliucord.manager.ui.screens.about.AboutModel
|
|||
import com.aliucord.manager.ui.screens.home.HomeModel
|
||||
import com.aliucord.manager.ui.screens.iconopts.IconOptionsModel
|
||||
import com.aliucord.manager.ui.screens.log.LogScreenModel
|
||||
import com.aliucord.manager.ui.screens.logs.LogsListScreenModel
|
||||
import com.aliucord.manager.ui.screens.patching.PatchingScreenModel
|
||||
import com.aliucord.manager.ui.screens.patchopts.PatchOptionsModel
|
||||
import com.aliucord.manager.ui.screens.plugins.PluginsModel
|
||||
|
|
@ -56,6 +57,7 @@ class ManagerApplication : Application() {
|
|||
factoryOf(::PatchOptionsModel)
|
||||
factoryOf(::IconOptionsModel)
|
||||
factoryOf(::LogScreenModel)
|
||||
factoryOf(::LogsListScreenModel)
|
||||
viewModelOf(::UpdaterViewModel)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,19 @@ class InstallLogManager(
|
|||
) {
|
||||
val logsDir = application.filesDir.resolve("install-logs").apply { mkdir() }
|
||||
|
||||
/**
|
||||
* 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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the install log from disk, if it exists.
|
||||
*/
|
||||
|
|
@ -46,6 +59,11 @@ class InstallLogManager(
|
|||
}
|
||||
}
|
||||
|
||||
fun deleteAllEntries() {
|
||||
logsDir.deleteRecursively()
|
||||
logsDir.mkdir()
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an install log entry to disk.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -27,4 +27,3 @@ private fun PluginsScreenNonePreview() {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.screens.about.AboutScreen
|
||||
import com.aliucord.manager.ui.screens.logs.LogsListScreen
|
||||
import com.aliucord.manager.ui.screens.settings.SettingsScreen
|
||||
|
||||
@Composable
|
||||
|
|
@ -19,14 +20,21 @@ fun HomeAppBar() {
|
|||
IconButton(onClick = { navigator?.push(AboutScreen()) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_info),
|
||||
contentDescription = stringResource(R.string.navigation_about)
|
||||
contentDescription = stringResource(R.string.navigation_about),
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { navigator?.push(LogsListScreen()) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_receipt),
|
||||
contentDescription = stringResource(R.string.navigation_about),
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = { navigator?.push(SettingsScreen()) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_settings),
|
||||
contentDescription = stringResource(R.string.navigation_settings)
|
||||
contentDescription = stringResource(R.string.navigation_settings),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import com.aliucord.manager.manager.InstallLogData
|
|||
import com.aliucord.manager.ui.components.Label
|
||||
import com.aliucord.manager.ui.components.back
|
||||
import com.aliucord.manager.ui.screens.log.components.LogAppBar
|
||||
import com.aliucord.manager.ui.screens.log.components.SelectableTextArea
|
||||
import com.aliucord.manager.ui.screens.log.components.LogTextArea
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.koin.core.parameter.parametersOf
|
||||
|
|
@ -73,7 +73,7 @@ fun LogScreenContent(
|
|||
name = stringResource(R.string.log_section_install_info),
|
||||
description = null,
|
||||
) {
|
||||
SelectableTextArea(
|
||||
LogTextArea(
|
||||
text = """
|
||||
Installation ID: ${data.id}
|
||||
Installation Date: ${data.installDate}
|
||||
|
|
@ -88,7 +88,7 @@ fun LogScreenContent(
|
|||
name = stringResource(R.string.log_section_env_info),
|
||||
description = null,
|
||||
) {
|
||||
SelectableTextArea(
|
||||
LogTextArea(
|
||||
text = data.environmentInfo,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
|
@ -101,7 +101,7 @@ fun LogScreenContent(
|
|||
name = stringResource(R.string.log_section_error),
|
||||
description = null,
|
||||
) {
|
||||
SelectableTextArea(
|
||||
LogTextArea(
|
||||
text = data.errorStacktrace,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
|
@ -114,7 +114,7 @@ fun LogScreenContent(
|
|||
name = stringResource(R.string.log_section_log),
|
||||
description = null,
|
||||
) {
|
||||
SelectableTextArea(
|
||||
LogTextArea(
|
||||
text = data.installationLog,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.aliucord.manager.ui.util.horizontalScrollbar
|
||||
import com.aliucord.manager.ui.util.thenIf
|
||||
|
||||
@Composable
|
||||
fun SelectableTextArea(
|
||||
fun LogTextArea(
|
||||
text: String,
|
||||
modifier: Modifier = Modifier.Companion,
|
||||
) {
|
||||
|
|
@ -26,11 +27,12 @@ fun SelectableTextArea(
|
|||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
lineHeight = 18.sp,
|
||||
fontFamily = FontFamily.Companion.Monospace,
|
||||
softWrap = false,
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(10.dp))
|
||||
.background(MaterialTheme.colorScheme.surfaceContainerHighest)
|
||||
.background(MaterialTheme.colorScheme.surfaceContainerHigh)
|
||||
.padding(start = 18.dp, end = 18.dp, top = 14.dp, bottom = 14.dp)
|
||||
.horizontalScroll(scrollState)
|
||||
.horizontalScrollbar(scrollState)
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package com.aliucord.manager.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.getScreenModel
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.aliucord.manager.ui.screens.log.LogScreen
|
||||
import com.aliucord.manager.ui.screens.logs.components.*
|
||||
import com.aliucord.manager.ui.screens.logs.components.dialogs.DeleteLogsDialog
|
||||
import com.aliucord.manager.ui.util.paddings.PaddingValuesSides
|
||||
import com.aliucord.manager.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 = getScreenModel<LogsListScreenModel>()
|
||||
|
||||
LogsScreenContent(
|
||||
logs = model.logEntries,
|
||||
onOpenLog = { navigator.push(LogScreen(installId = it)) },
|
||||
onDeleteLogs = model::deleteLogs, // TODO: show warning dialog first
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LogsScreenContent(
|
||||
logs: SnapshotStateList<LogEntry>,
|
||||
onOpenLog: (id: String) -> Unit,
|
||||
onDeleteLogs: () -> Unit,
|
||||
) {
|
||||
var showWipeConfirmDialog by remember { mutableStateOf(false) }
|
||||
|
||||
if (showWipeConfirmDialog) {
|
||||
DeleteLogsDialog(
|
||||
onConfirm = {
|
||||
showWipeConfirmDialog = false
|
||||
onDeleteLogs()
|
||||
},
|
||||
onDismiss = {
|
||||
showWipeConfirmDialog = false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
LogsListAppBar(
|
||||
onDeleteLogs = { showWipeConfirmDialog = true },
|
||||
)
|
||||
},
|
||||
) { 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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.aliucord.manager.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.aliucord.manager.R
|
||||
import com.aliucord.manager.manager.InstallLogManager
|
||||
import com.aliucord.manager.util.launchBlock
|
||||
import com.aliucord.manager.util.showToast
|
||||
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.launchBlock {
|
||||
logsManager.deleteAllEntries()
|
||||
logEntries.clear()
|
||||
application.showToast(R.string.logs_status_delete_success)
|
||||
}
|
||||
|
||||
private fun loadLogsList() = screenModelScope.launchBlock {
|
||||
for (installId in logsManager.fetchInstallDataEntries()) {
|
||||
val data = logsManager.fetchInstallData(id = installId)
|
||||
?: continue
|
||||
|
||||
logEntries += 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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
data class LogEntry(
|
||||
val id: String,
|
||||
val isError: Boolean,
|
||||
val installDate: String,
|
||||
val durationSecs: Float,
|
||||
val stacktracePreview: ImmutableList<String>?,
|
||||
)
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package com.aliucord.manager.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.aliucord.manager.R
|
||||
import com.aliucord.manager.patcher.steps.base.StepState
|
||||
import com.aliucord.manager.ui.screens.logs.LogEntry
|
||||
import com.aliucord.manager.ui.screens.patching.components.StepStateIcon
|
||||
import com.aliucord.manager.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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.aliucord.manager.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.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.components.BackButton
|
||||
import com.aliucord.manager.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)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package com.aliucord.manager.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.aliucord.manager.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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.aliucord.manager.ui.screens.logs.components.dialogs
|
||||
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.aliucord.manager.R
|
||||
|
||||
@Composable
|
||||
fun DeleteLogsDialog(
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
icon = {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_delete_forever),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
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))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -134,40 +134,40 @@ class PatchingScreenModel(
|
|||
delay(400)
|
||||
|
||||
// Execute all the steps and catch any errors
|
||||
when (val error = runner.executeAll()) {
|
||||
val error = when (val error = runner.executeAll()) {
|
||||
null -> {
|
||||
// If install step is marked skipped then the installation was manually aborted
|
||||
// and if so, immediately close install screen
|
||||
if (runner.getStep<InstallStep>().state == StepState.Skipped) {
|
||||
mutableState.value = PatchingScreenState.CloseScreen
|
||||
|
||||
Error("Installation was aborted or cancelled")
|
||||
.apply { stackTrace = emptyArray() }
|
||||
}
|
||||
// At this point, the installation has successfully completed
|
||||
else {
|
||||
mutableState.value = PatchingScreenState.Success
|
||||
installLogs.storeInstallData(
|
||||
id = installId!!,
|
||||
installDate = startTime!!,
|
||||
installDuration = runner.steps.sumOf { it.getDuration() }.milliseconds,
|
||||
options = options,
|
||||
log = runner.getLog(),
|
||||
error = null,
|
||||
)
|
||||
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
Log.e(BuildConfig.TAG, "Failed to perform installation process", error)
|
||||
mutableState.value = PatchingScreenState.Failed(installId = installId!!)
|
||||
installLogs.storeInstallData(
|
||||
id = installId!!,
|
||||
installDate = startTime!!,
|
||||
installDuration = runner.steps.sumOf { it.getDuration() }.milliseconds,
|
||||
options = options,
|
||||
log = runner.getLog(),
|
||||
error = error,
|
||||
)
|
||||
|
||||
error
|
||||
}
|
||||
}
|
||||
|
||||
installLogs.storeInstallData(
|
||||
id = installId!!,
|
||||
installDate = startTime!!,
|
||||
installDuration = runner.steps.sumOf { it.getDuration() }.milliseconds,
|
||||
options = options,
|
||||
log = runner.getLog(),
|
||||
error = error,
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.compose.material3.*
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.aliucord.manager.R
|
||||
|
||||
@Composable
|
||||
|
|
@ -24,13 +25,16 @@ fun UninstallPluginDialog(
|
|||
Text(stringResource(R.string.plugins_delete_plugin, pluginName))
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.plugins_delete_plugin_body))
|
||||
Text(
|
||||
text = stringResource(R.string.plugins_delete_plugin_body),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = onConfirm,
|
||||
) {
|
||||
Text(stringResource(android.R.string.ok))
|
||||
Text(stringResource(R.string.action_confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
|
|
@ -41,7 +45,7 @@ fun UninstallPluginDialog(
|
|||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||
)
|
||||
) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
Text(stringResource(R.string.action_cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
9
app/src/main/res/drawable/ic_receipt.xml
Normal file
9
app/src/main/res/drawable/ic_receipt.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<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>
|
||||
9
app/src/main/res/drawable/ic_reciept_off.xml
Normal file
9
app/src/main/res/drawable/ic_reciept_off.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<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>
|
||||
|
|
@ -70,6 +70,7 @@
|
|||
|
||||
<string name="navigation_back">Back</string>
|
||||
<string name="navigation_about">About</string>
|
||||
<string name="navigation_logs">Open logs</string>
|
||||
<string name="navigation_settings">Settings</string>
|
||||
|
||||
<string name="contributors_lead">Lead</string>
|
||||
|
|
@ -194,6 +195,13 @@
|
|||
<string name="log_action_export">Export log</string>
|
||||
<string name="log_action_share">Share log</string>
|
||||
|
||||
<string name="logs_title">Logs</string>
|
||||
<string name="logs_none">No logs present!</string>
|
||||
<string name="logs_action_delete_all">Delete all logs</string>
|
||||
<string name="logs_status_delete_success">Successfully deleted all logs</string>
|
||||
<string name="logs_wipe_title">Clear Logs</string>
|
||||
<string name="logs_wipe_desc">Are you sure you want to permanently delete all the logs?</string>
|
||||
|
||||
<string name="notif_group_install_title">Active installation</string>
|
||||
<string name="notif_group_install_desc">Progress notifications sent during a minimized installation</string>
|
||||
<string name="notif_install_ready_title">Aliucord installation ready!</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue