diff --git a/app/src/main/java/app/revanced/manager/compose/ui/screen/InstallerScreen.kt b/app/src/main/java/app/revanced/manager/compose/ui/screen/InstallerScreen.kt index b316f6ae..fb24c018 100644 --- a/app/src/main/java/app/revanced/manager/compose/ui/screen/InstallerScreen.kt +++ b/app/src/main/java/app/revanced/manager/compose/ui/screen/InstallerScreen.kt @@ -2,21 +2,40 @@ package app.revanced.manager.compose.ui.screen import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts.CreateDocument -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Cancel +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.KeyboardArrowDown +import androidx.compose.material.icons.filled.KeyboardArrowUp +import androidx.compose.material.icons.outlined.HelpOutline +import androidx.compose.material.icons.outlined.MoreVert +import androidx.compose.material3.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import app.revanced.manager.compose.R +import app.revanced.manager.compose.patcher.worker.StepGroup +import app.revanced.manager.compose.patcher.worker.StepStatus import app.revanced.manager.compose.ui.component.AppScaffold import app.revanced.manager.compose.ui.component.AppTopBar import app.revanced.manager.compose.ui.viewmodel.InstallerScreenViewModel import app.revanced.manager.compose.util.APK_MIMETYPE +import kotlin.math.floor @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -31,6 +50,14 @@ fun InstallerScreen( AppTopBar( title = stringResource(R.string.installer), onBackClick = onBackClick, + actions = { + IconButton(onClick = {}) { + Icon(Icons.Outlined.HelpOutline, stringResource(R.string.help)) + } + IconButton(onClick = {}) { + Icon(Icons.Outlined.MoreVert, stringResource(R.string.more)) + } + } ) } ) { paddingValues -> @@ -40,34 +67,139 @@ fun InstallerScreen( .fillMaxSize() ) { vm.stepGroups.forEach { - Column { - Text( - text = "${stringResource(it.name)}: ${it.status}", - style = MaterialTheme.typography.titleLarge - ) + InstallGroup(it) + } + Spacer(modifier = Modifier.weight(1f)) + Row( + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.spacedBy(12.dp, Alignment.End), + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) + ) { + Button( + onClick = { exportApkLauncher.launch("${vm.packageName}.apk") }, + enabled = vm.canInstall + ) { + Text(stringResource(R.string.export_app)) + } + + Button( + onClick = vm::installApk, + enabled = vm.canInstall + ) { + Text(stringResource(R.string.install_app)) + } + } + } + } +} + +// Credits: https://github.com/Aliucord/AliucordManager/blob/main/app/src/main/kotlin/com/aliucord/manager/ui/component/installer/InstallGroup.kt + +@Composable +fun InstallGroup(group: StepGroup) { + var expanded by rememberSaveable { mutableStateOf(true) } + Column( + modifier = Modifier + .padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 8.dp) + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .run { + if (expanded) { + background(MaterialTheme.colorScheme.secondaryContainer) + } else this + } + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp) + .run { if (expanded) { + background(MaterialTheme.colorScheme.secondaryContainer) + } else + background(MaterialTheme.colorScheme.surface) + } + ) { + StepIcon(group.status, 24.dp) + + Text(text = stringResource(group.name), style = MaterialTheme.typography.titleMedium) + + Spacer(modifier = Modifier.weight(1f)) + + IconButton(onClick = { expanded = !expanded }) { + if (expanded) { + Icon( + imageVector = Icons.Filled.KeyboardArrowUp, + contentDescription = "collapse" + ) + } else { + Icon( + imageVector = Icons.Filled.KeyboardArrowDown, + contentDescription = "expand" + ) + } + } + } + + AnimatedVisibility(visible = expanded) { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier + .background(MaterialTheme.colorScheme.background.copy(0.6f)) + .fillMaxWidth() + .padding(16.dp) + .padding(start = 4.dp) + ) { + group.steps.forEach { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + StepIcon(it.status, size = 18.dp) - it.steps.forEach { Text( - text = "${it.name}: ${it.status}", - style = MaterialTheme.typography.titleMedium + text = it.name, + style = MaterialTheme.typography.titleSmall, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f, true), ) } } } - - Button( - onClick = vm::installApk, - enabled = vm.canInstall - ) { - Text(stringResource(R.string.install_app)) - } - - Button( - onClick = { exportApkLauncher.launch("${vm.packageName}.apk") }, - enabled = vm.canInstall - ) { - Text(stringResource(R.string.export_app)) - } } } -} \ No newline at end of file +} + +@Composable +fun StepIcon(status: StepStatus, size: Dp) { + val strokeWidth = Dp(floor(size.value / 10) + 1) + + when (status) { + StepStatus.COMPLETED -> Icon( + Icons.Filled.CheckCircle, + contentDescription = "success", + tint = MaterialTheme.colorScheme.surfaceTint, + modifier = Modifier.size(size) + ) + + StepStatus.FAILURE -> Icon( + Icons.Filled.Cancel, + contentDescription = "failed", + tint = MaterialTheme.colorScheme.error, + modifier = Modifier.size(size) + ) + + StepStatus.WAITING -> CircularProgressIndicator( + strokeWidth = strokeWidth, + modifier = Modifier + .size(size) + .semantics { + contentDescription = "waiting" + } + ) + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8cce8c79..415547bc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -76,6 +76,7 @@ Patching Saving Write patched Apk + More Donate Website GitHub