mirror of
https://github.com/topjohnwu/Magisk.git
synced 2026-07-28 14:47:21 -07:00
Manual UI tweaking
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
package com.topjohnwu.magisk.ui.home
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.PowerManager
|
||||
@@ -9,17 +7,12 @@ import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -28,6 +21,35 @@ import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Layers
|
||||
import androidx.compose.material.icons.filled.PowerSettingsNew
|
||||
import androidx.compose.material.icons.filled.PushPin
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
@@ -42,16 +64,14 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
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.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.net.toUri
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.core.BuildConfig
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
@@ -68,43 +88,12 @@ import com.topjohnwu.magisk.ui.component.ConfirmResult
|
||||
import com.topjohnwu.magisk.ui.component.LoadingDialogHandle
|
||||
import com.topjohnwu.magisk.ui.component.MarkdownText
|
||||
import com.topjohnwu.magisk.ui.component.MarkdownTextAsync
|
||||
import com.topjohnwu.magisk.ui.component.SettingsArrow
|
||||
import com.topjohnwu.magisk.ui.component.rememberConfirmDialog
|
||||
import com.topjohnwu.magisk.ui.component.rememberLoadingDialog
|
||||
import com.topjohnwu.magisk.ui.flash.FlashUtils
|
||||
import com.topjohnwu.magisk.ui.install.InstallViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.PushPin
|
||||
import androidx.compose.material.icons.filled.Layers
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.VerticalDivider
|
||||
import com.topjohnwu.magisk.ui.component.SettingsArrow
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
|
||||
|
||||
@@ -118,7 +107,6 @@ fun HomeScreen(viewModel: HomeViewModel, installVm: InstallViewModel) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
val scope = rememberCoroutineScope()
|
||||
val loadingDialog = rememberLoadingDialog()
|
||||
val navigator = com.topjohnwu.magisk.ui.navigation.LocalNavigator.current
|
||||
|
||||
val showUninstallDialog = rememberSaveable { mutableStateOf(false) }
|
||||
val showManagerDialog = rememberSaveable { mutableStateOf(false) }
|
||||
@@ -263,16 +251,7 @@ fun HomeScreen(viewModel: HomeViewModel, installVm: InstallViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = viewModel.magiskState,
|
||||
version = viewModel.magiskInstalledVersion,
|
||||
remoteVersion = if (viewModel.magiskState == HomeViewModel.State.OUTDATED)
|
||||
"${BuildConfig.APP_VERSION_NAME} (${BuildConfig.APP_VERSION_CODE})" else null,
|
||||
onInstallClicked = { showInstallSheet.value = true },
|
||||
onUninstallClicked = { viewModel.onDeletePressed() },
|
||||
)
|
||||
|
||||
UninstallButton(
|
||||
onClick = { viewModel.onDeletePressed() },
|
||||
enabled = Info.env.isActive
|
||||
)
|
||||
) { showInstallSheet.value = true }
|
||||
|
||||
StatusCard()
|
||||
|
||||
@@ -280,15 +259,17 @@ fun HomeScreen(viewModel: HomeViewModel, installVm: InstallViewModel) {
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = uiState.appState,
|
||||
version = viewModel.managerInstalledVersion,
|
||||
remoteVersion = if (uiState.appState == HomeViewModel.State.OUTDATED)
|
||||
uiState.managerRemoteVersion else null,
|
||||
remoteVersion = uiState.managerRemoteVersion,
|
||||
progress = uiState.managerProgress,
|
||||
isHidden = context.packageName != BuildConfig.APP_PACKAGE_NAME,
|
||||
onManagerPressed = { viewModel.onManagerPressed() },
|
||||
onManagerPressed = viewModel::onManagerPressed,
|
||||
onHideRestorePressed = viewModel::onHideRestorePressed,
|
||||
)
|
||||
|
||||
val showDonateSheet = rememberSaveable { mutableStateOf(false) }
|
||||
UninstallButton(
|
||||
onClick = { viewModel.onDeletePressed() },
|
||||
enabled = Info.env.isActive
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(CoreR.string.home_support_title),
|
||||
@@ -296,46 +277,15 @@ fun HomeScreen(viewModel: HomeViewModel, installVm: InstallViewModel) {
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 4.dp)
|
||||
)
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(CoreR.string.home_support_content),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
IconButton(onClick = { viewModel.onLinkPressed(Const.Url.PATREON_URL) }) {
|
||||
Icon(
|
||||
painter = painterResource(CoreR.drawable.ic_patreon),
|
||||
contentDescription = "Patreon",
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { viewModel.onLinkPressed("https://paypal.me/magiskdonate") }) {
|
||||
Icon(
|
||||
painter = painterResource(CoreR.drawable.ic_paypal),
|
||||
contentDescription = "PayPal",
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SupportCard(onLinkClicked = viewModel::onLinkPressed)
|
||||
|
||||
Text(
|
||||
text = stringResource(CoreR.string.home_follow_title),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.padding(start = 24.dp, top = 24.dp, bottom = 8.dp)
|
||||
modifier = Modifier.padding(start = 16.dp, top = 8.dp, bottom = 4.dp)
|
||||
)
|
||||
DevelopersCard(onLinkClicked = { openLink(context, it) })
|
||||
DevelopersCard(onLinkClicked = viewModel::onLinkPressed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +330,7 @@ private fun RebootButton() {
|
||||
onClick = { showMenu.value = true },
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_restart),
|
||||
imageVector = Icons.Default.PowerSettingsNew,
|
||||
contentDescription = stringResource(CoreR.string.reboot),
|
||||
)
|
||||
}
|
||||
@@ -425,7 +375,9 @@ private fun NoticeCard(onHide: () -> Unit) {
|
||||
text = stringResource(CoreR.string.home_notice_content),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onTertiaryContainer,
|
||||
modifier = Modifier.weight(1f).padding(vertical = 8.dp)
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = 8.dp)
|
||||
)
|
||||
IconButton(onClick = onHide) {
|
||||
Icon(
|
||||
@@ -439,14 +391,36 @@ private fun NoticeCard(onHide: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InstallButton(
|
||||
label: String,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Button(
|
||||
onClick = onClick,
|
||||
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.secondary),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_download_md2),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
text = label,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CoreCard(
|
||||
modifier: Modifier = Modifier,
|
||||
state: HomeViewModel.State,
|
||||
version: String,
|
||||
remoteVersion: String? = null,
|
||||
onInstallClicked: () -> Unit,
|
||||
onUninstallClicked: () -> Unit,
|
||||
) {
|
||||
val actionLabel = when (state) {
|
||||
HomeViewModel.State.OUTDATED -> stringResource(CoreR.string.update)
|
||||
@@ -478,37 +452,21 @@ private fun CoreCard(
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(CoreR.string.home_core_title),
|
||||
text = stringResource(CoreR.string.magisk),
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
Text(
|
||||
text = version.ifEmpty { stringResource(CoreR.string.not_available) },
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (actionLabel != null) {
|
||||
Button(
|
||||
InstallButton(
|
||||
label = actionLabel,
|
||||
onClick = onInstallClicked,
|
||||
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_download_md2),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
tint = Color.White
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
text = actionLabel,
|
||||
color = Color.White,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -547,7 +505,7 @@ private fun AppCard(
|
||||
modifier: Modifier = Modifier,
|
||||
state: HomeViewModel.State,
|
||||
version: String,
|
||||
remoteVersion: String? = null,
|
||||
remoteVersion: String,
|
||||
progress: Int,
|
||||
isHidden: Boolean,
|
||||
onManagerPressed: () -> Unit,
|
||||
@@ -594,44 +552,30 @@ private fun AppCard(
|
||||
imageVector = hideRestoreIcon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (actionLabel != null) {
|
||||
Button(
|
||||
InstallButton(
|
||||
label = actionLabel,
|
||||
onClick = onManagerPressed,
|
||||
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_update_md2),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp),
|
||||
tint = Color.White
|
||||
)
|
||||
Spacer(Modifier.width(4.dp))
|
||||
Text(
|
||||
text = actionLabel,
|
||||
color = Color.White,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
|
||||
AppDetailRow(label = stringResource(CoreR.string.home_latest_version), value = remoteVersion ?: version)
|
||||
if (state != HomeViewModel.State.LOADING) {
|
||||
AppDetailRow(label = stringResource(CoreR.string.home_latest_version), value = remoteVersion)
|
||||
}
|
||||
AppDetailRow(label = stringResource(CoreR.string.home_installed_version), value = version)
|
||||
AppDetailRow(label = stringResource(CoreR.string.home_package), value = LocalContext.current.packageName)
|
||||
|
||||
if (progress in 1..99) {
|
||||
Spacer(Modifier.height(8.dp))
|
||||
LinearProgressIndicator(
|
||||
progress = progress / 100f,
|
||||
progress = { progress / 100f },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
@@ -652,19 +596,6 @@ private fun AppDetailRow(label: String, value: String) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpdateBadge(version: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = version,
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
fontSize = 10.sp,
|
||||
maxLines = 1,
|
||||
modifier = modifier
|
||||
.background(MaterialTheme.colorScheme.primary, RoundedCornerShape(6.dp))
|
||||
.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StatusCard() {
|
||||
Row(
|
||||
@@ -712,7 +643,9 @@ private fun StatusItemCard(
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp).rotate(if (icon == Icons.Filled.PushPin) 45f else 0f)
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.rotate(if (icon == Icons.Filled.PushPin) 45f else 0f)
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(
|
||||
@@ -723,58 +656,37 @@ private fun StatusItemCard(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun SupportBottomSheet(
|
||||
show: MutableState<Boolean>,
|
||||
onLinkClicked: (String) -> Unit,
|
||||
) {
|
||||
if (show.value) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { show.value = false },
|
||||
) {
|
||||
private fun SupportCard(onLinkClicked: (String) -> Unit) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(CoreR.string.home_support_title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
text = stringResource(CoreR.string.home_support_content),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Column(modifier = Modifier.padding(bottom = 16.dp)) {
|
||||
Text(
|
||||
text = stringResource(CoreR.string.home_support_content),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
)
|
||||
SettingsArrow(
|
||||
title = stringResource(CoreR.string.patreon),
|
||||
onClick = {
|
||||
show.value = false
|
||||
onLinkClicked(Const.Url.PATREON_URL)
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(CoreR.drawable.ic_patreon),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
)
|
||||
SettingsArrow(
|
||||
title = stringResource(CoreR.string.paypal),
|
||||
onClick = {
|
||||
show.value = false
|
||||
onLinkClicked("https://paypal.me/magiskdonate")
|
||||
},
|
||||
leadingContent = {
|
||||
Icon(
|
||||
painter = painterResource(CoreR.drawable.ic_paypal),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
IconButton(onClick = { onLinkClicked(Const.Url.PATREON_URL) }) {
|
||||
Icon(
|
||||
painter = painterResource(CoreR.drawable.ic_patreon),
|
||||
contentDescription = "Patreon",
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { onLinkClicked("https://paypal.me/magiskdonate") }) {
|
||||
Icon(
|
||||
painter = painterResource(CoreR.drawable.ic_paypal),
|
||||
contentDescription = "PayPal",
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -851,14 +763,6 @@ private fun DevelopersCard(onLinkClicked: (String) -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun openLink(context: Context, url: String) {
|
||||
try {
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, url.toUri()).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
})
|
||||
} catch (_: ActivityNotFoundException) { }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun InstallBottomSheet(
|
||||
|
||||
+108
-137
@@ -1,19 +1,31 @@
|
||||
package com.topjohnwu.magisk.ui.superuser
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Cancel
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -24,22 +36,12 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.topjohnwu.magisk.ui.component.ConfirmResult
|
||||
import com.topjohnwu.magisk.ui.component.SettingsSwitch
|
||||
import com.topjohnwu.magisk.ui.component.rememberConfirmDialog
|
||||
import com.topjohnwu.magisk.ui.util.rememberDrawablePainter
|
||||
import kotlinx.coroutines.launch
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -81,103 +83,83 @@ fun SuperuserDetailScreen(
|
||||
) { padding ->
|
||||
if (item == null) return@Scaffold
|
||||
|
||||
LazyColumn(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(padding)
|
||||
.padding(horizontal = 12.dp),
|
||||
contentPadding = PaddingValues(bottom = 88.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
.padding(horizontal = 12.dp)
|
||||
.padding(bottom = 88.dp)
|
||||
) {
|
||||
item {
|
||||
Spacer(Modifier.height(4.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = rememberDrawablePainter(item.icon),
|
||||
contentDescription = item.appName,
|
||||
modifier = Modifier.size(48.dp)
|
||||
)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Column {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = item.title,
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
modifier = Modifier.weight(1f, fill = false),
|
||||
)
|
||||
if (item.isSharedUid) {
|
||||
Spacer(Modifier.width(6.dp))
|
||||
SharedUidBadge()
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = item.packageName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
text = "UID: ${item.policy.uid}",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Column {
|
||||
if (uiState.suRestrict || item.isRestricted) {
|
||||
SwitchRow(
|
||||
title = stringResource(CoreR.string.settings_su_restrict_title),
|
||||
checked = item.isRestricted,
|
||||
onCheckedChange = { viewModel.toggleRestrict(item) }
|
||||
)
|
||||
}
|
||||
SwitchRow(
|
||||
title = stringResource(CoreR.string.superuser_toggle_notification),
|
||||
checked = item.notification,
|
||||
onCheckedChange = { viewModel.updateNotify(item) }
|
||||
)
|
||||
SwitchRow(
|
||||
title = stringResource(CoreR.string.logs),
|
||||
checked = item.logging,
|
||||
onCheckedChange = { viewModel.updateLogging(item) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
if (viewModel.requiresAuth) {
|
||||
viewModel.authenticate { viewModel.performDelete(item, onBack) }
|
||||
} else {
|
||||
scope.launch {
|
||||
val result = revokeDialog.awaitConfirm(
|
||||
title = revokeTitle,
|
||||
content = revokeMsg,
|
||||
)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
viewModel.performDelete(item, onBack)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RevokeRow()
|
||||
Image(
|
||||
painter = rememberDrawablePainter(item.icon),
|
||||
contentDescription = item.appName,
|
||||
modifier = Modifier.size(48.dp)
|
||||
)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Column {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = item.title,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier.weight(1f, fill = false),
|
||||
)
|
||||
if (item.isSharedUid) {
|
||||
Spacer(Modifier.width(8.dp))
|
||||
SharedUidBadge()
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = item.packageName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
text = "UID: ${item.policy.uid}",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uiState.suRestrict || item.isRestricted) {
|
||||
SettingsSwitch(
|
||||
title = stringResource(CoreR.string.settings_su_restrict_title),
|
||||
checked = item.isRestricted,
|
||||
onCheckedChange = { viewModel.toggleRestrict(item) }
|
||||
)
|
||||
}
|
||||
SettingsSwitch(
|
||||
title = stringResource(CoreR.string.superuser_toggle_notification),
|
||||
checked = item.notification,
|
||||
onCheckedChange = { viewModel.updateNotify(item) }
|
||||
)
|
||||
SettingsSwitch(
|
||||
title = stringResource(CoreR.string.logs),
|
||||
checked = item.logging,
|
||||
onCheckedChange = { viewModel.updateLogging(item) }
|
||||
)
|
||||
|
||||
RevokeButton {
|
||||
if (viewModel.requiresAuth) {
|
||||
viewModel.authenticate { viewModel.performDelete(item, onBack) }
|
||||
} else {
|
||||
scope.launch {
|
||||
val result = revokeDialog.awaitConfirm(
|
||||
title = revokeTitle,
|
||||
content = revokeMsg,
|
||||
)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
viewModel.performDelete(item, onBack)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,38 +167,27 @@ fun SuperuserDetailScreen(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SwitchRow(
|
||||
title: String,
|
||||
checked: Boolean,
|
||||
onCheckedChange: () -> Unit,
|
||||
private fun RevokeButton(
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 14.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
Button(
|
||||
onClick = onClick,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = MaterialTheme.colorScheme.errorContainer,
|
||||
contentColor = MaterialTheme.colorScheme.onErrorContainer
|
||||
),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.weight(1f),
|
||||
Icon(
|
||||
imageVector = Icons.Default.Cancel,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Switch(
|
||||
checked = checked,
|
||||
onCheckedChange = { onCheckedChange() }
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(
|
||||
text = stringResource(CoreR.string.superuser_toggle_revoke),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RevokeRow() {
|
||||
Text(
|
||||
text = stringResource(CoreR.string.superuser_toggle_revoke),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,20 +19,7 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.topjohnwu.magisk.ui.navigation.LocalNavigator
|
||||
import com.topjohnwu.magisk.ui.navigation.Route
|
||||
import com.topjohnwu.magisk.ui.util.rememberDrawablePainter
|
||||
import androidx.compose.material3.Badge
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -42,6 +29,18 @@ import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.topjohnwu.magisk.ui.navigation.LocalNavigator
|
||||
import com.topjohnwu.magisk.ui.navigation.Route
|
||||
import com.topjohnwu.magisk.ui.util.rememberDrawablePainter
|
||||
import com.topjohnwu.magisk.core.R as CoreR
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -184,13 +183,10 @@ private fun PolicyCard(
|
||||
|
||||
@Composable
|
||||
internal fun SharedUidBadge(modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = "SharedUID",
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
fontSize = 10.sp,
|
||||
maxLines = 1,
|
||||
modifier = modifier
|
||||
.background(MaterialTheme.colorScheme.primary, RoundedCornerShape(6.dp))
|
||||
.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||
)
|
||||
Badge(
|
||||
modifier = modifier,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
) {
|
||||
Text("SharedUID")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user