Switch to Google Material 3 library

Made-with: Gemini
This commit is contained in:
topjohnwu
2026-03-18 09:59:47 -07:00
committed by John Wu
parent 9035a94804
commit e2a437ebfc
19 changed files with 1032 additions and 1012 deletions

View File

@@ -46,16 +46,16 @@ dependencies {
implementation(libs.compose.ui)
implementation(libs.compose.ui.tooling.preview)
debugImplementation(libs.compose.ui.tooling)
implementation(libs.compose.material.icons.extended)
implementation(libs.activity.compose)
implementation(libs.lifecycle.runtime.compose)
implementation(libs.lifecycle.viewmodel.compose)
implementation(libs.miuix)
implementation(libs.miuix.icons)
implementation(libs.miuix.navigation3.ui)
implementation(libs.compose.material3)
// Navigation3
implementation(libs.navigation3.runtime)
implementation(libs.navigationevent.compose)
implementation(libs.lifecycle.viewmodel.navigation3)
implementation(libs.navigation3.ui)
}

View File

@@ -63,7 +63,7 @@ import com.topjohnwu.magisk.ui.theme.Theme
import com.topjohnwu.magisk.view.Shortcuts
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import top.yukonga.miuix.kmp.utils.MiuixPopupUtils.Companion.MiuixPopupHost
import com.topjohnwu.magisk.core.R as CoreR
class MainActivity : AppCompatActivity(), SplashScreenHost {
@@ -162,7 +162,7 @@ class MainActivity : AppCompatActivity(), SplashScreenHost {
onBack = { navigator.pop() },
entryDecorators = listOf(
rememberSaveableStateHolderNavEntryDecorator(),
rememberViewModelStoreNavEntryDecorator()
rememberViewModelStoreNavEntryDecorator<Any>()
),
entryProvider = entryProvider {
entry<Route.Main> {
@@ -210,7 +210,6 @@ class MainActivity : AppCompatActivity(), SplashScreenHost {
)
}
MainActivityDialogs(activity = this@MainActivity)
MiuixPopupHost()
}
}
}

View File

@@ -57,9 +57,9 @@ import com.topjohnwu.magisk.ui.settings.SettingsViewModel
import com.topjohnwu.magisk.ui.superuser.SuperuserScreen
import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel
import kotlinx.coroutines.launch
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import com.topjohnwu.magisk.core.R as CoreR
enum class Tab(val titleRes: Int, val iconRes: Int) {
@@ -160,7 +160,7 @@ private fun FloatingNavigationBar(
.padding(bottom = navBarInset + 12.dp, start = 24.dp, end = 24.dp)
.shadow(elevation = 6.dp, shape = shape)
.clip(shape)
.background(MiuixTheme.colorScheme.surfaceContainer)
.background(MaterialTheme.colorScheme.surfaceContainer)
.fillMaxWidth()
.height(64.dp)
.padding(horizontal = 4.dp),
@@ -191,9 +191,9 @@ private fun FloatingNavItem(
) {
val contentColor by animateColorAsState(
targetValue = when {
!enabled -> MiuixTheme.colorScheme.disabledOnSecondaryVariant
selected -> MiuixTheme.colorScheme.primary
else -> MiuixTheme.colorScheme.onSurfaceVariantActions
!enabled -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
selected -> MaterialTheme.colorScheme.primary
else -> MaterialTheme.colorScheme.onSurfaceVariant
},
animationSpec = tween(200),
label = "navItemColor"

View File

@@ -43,12 +43,15 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import timber.log.Timber
import top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.InfiniteProgressIndicator
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TextButton
import top.yukonga.miuix.kmp.extra.SuperDialog
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import java.io.IOException
import kotlin.coroutines.resume
@@ -227,29 +230,36 @@ fun rememberConfirmDialog(callback: ConfirmCallback): ConfirmDialogHandle {
@Composable
private fun LoadingDialog(showDialog: MutableState<Boolean>) {
SuperDialog(
show = showDialog,
onDismissRequest = {},
content = {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.CenterStart
if (showDialog.value) {
Dialog(
onDismissRequest = {},
properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false)
) {
Surface(
shape = MaterialTheme.shapes.extraLarge,
tonalElevation = 6.dp,
modifier = Modifier.fillMaxWidth()
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start,
Box(
modifier = Modifier.padding(24.dp).fillMaxWidth(),
contentAlignment = Alignment.CenterStart
) {
InfiniteProgressIndicator(
color = MiuixTheme.colorScheme.onBackground
)
Text(
modifier = Modifier.padding(start = 12.dp),
text = stringResource(com.topjohnwu.magisk.core.R.string.loading),
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start,
) {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.onSurface
)
Text(
modifier = Modifier.padding(start = 16.dp),
text = stringResource(com.topjohnwu.magisk.core.R.string.loading),
)
}
}
}
}
)
}
}
@Composable
@@ -259,77 +269,59 @@ private fun ConfirmDialogContent(
dismiss: () -> Unit,
showDialog: MutableState<Boolean>
) {
SuperDialog(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)),
show = showDialog,
title = visuals.title,
onDismissRequest = {
dismiss()
showDialog.value = false
},
content = {
Layout(
content = {
visuals.content?.let { content ->
if (visuals.markdown) {
MarkdownText(content)
} else {
Text(
text = content,
color = MiuixTheme.colorScheme.onSurface,
)
}
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.padding(top = 12.dp)
) {
TextButton(
text = visuals.dismiss
?: stringResource(android.R.string.cancel),
onClick = {
dismiss()
showDialog.value = false
},
modifier = Modifier.weight(1f)
)
Spacer(Modifier.width(20.dp))
TextButton(
text = visuals.confirm
?: stringResource(android.R.string.ok),
onClick = {
confirm()
showDialog.value = false
},
modifier = Modifier.weight(1f),
colors = ButtonDefaults.textButtonColorsPrimary()
if (showDialog.value) {
AlertDialog(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)),
onDismissRequest = {
dismiss()
showDialog.value = false
},
title = if (visuals.title.isNotEmpty()) {
{ Text(text = visuals.title) }
} else null,
text = {
visuals.content?.let { content ->
if (visuals.markdown) {
MarkdownText(content)
} else {
Text(
text = content,
color = MaterialTheme.colorScheme.onSurface,
)
}
}
) { measurables, constraints ->
if (measurables.size != 2) {
val button = measurables[0].measure(constraints)
layout(constraints.maxWidth, button.height) {
button.place(0, 0)
},
confirmButton = {
TextButton(
onClick = {
confirm()
showDialog.value = false
}
} else {
val button = measurables[1].measure(constraints)
val content = measurables[0].measure(
constraints.copy(maxHeight = constraints.maxHeight - button.height)
) {
Text(
text = visuals.confirm ?: stringResource(android.R.string.ok)
)
layout(constraints.maxWidth, content.height + button.height) {
content.place(0, 0)
button.place(0, content.height)
}
},
dismissButton = {
TextButton(
onClick = {
dismiss()
showDialog.value = false
}
) {
Text(
text = visuals.dismiss ?: stringResource(android.R.string.cancel)
)
}
}
}
)
)
}
}
@Composable
fun MarkdownText(text: String) {
val contentColor = MiuixTheme.colorScheme.onBackground.toArgb()
val contentColor = MaterialTheme.colorScheme.onBackground.toArgb()
AndroidView(
factory = { context ->
TextView(context).apply {
@@ -368,7 +360,7 @@ fun MarkdownTextAsync(getMarkdownText: suspend () -> String) {
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
InfiniteProgressIndicator(color = MiuixTheme.colorScheme.onBackground)
CircularProgressIndicator(color = MaterialTheme.colorScheme.onBackground)
}
}
}

View File

@@ -1,84 +0,0 @@
package com.topjohnwu.magisk.ui.component
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
object ListPopupDefaults {
val MenuPositionProvider = object : PopupPositionProvider {
override fun calculatePosition(
anchorBounds: IntRect,
windowBounds: IntRect,
layoutDirection: LayoutDirection,
popupContentSize: IntSize,
popupMargin: IntRect,
alignment: PopupPositionProvider.Align,
): IntOffset {
val resolved = alignment.resolve(layoutDirection)
val offsetX: Int
val offsetY: Int
when (resolved) {
PopupPositionProvider.Align.TopStart -> {
offsetX = anchorBounds.left + popupMargin.left
offsetY = anchorBounds.bottom + popupMargin.top
}
PopupPositionProvider.Align.TopEnd -> {
offsetX = anchorBounds.right - popupContentSize.width - popupMargin.right
offsetY = anchorBounds.bottom + popupMargin.top
}
PopupPositionProvider.Align.BottomStart -> {
offsetX = anchorBounds.left + popupMargin.left
offsetY = anchorBounds.top - popupContentSize.height - popupMargin.bottom
}
PopupPositionProvider.Align.BottomEnd -> {
offsetX = anchorBounds.right - popupContentSize.width - popupMargin.right
offsetY = anchorBounds.top - popupContentSize.height - popupMargin.bottom
}
else -> {
offsetX = if (resolved == PopupPositionProvider.Align.End) {
anchorBounds.right - popupContentSize.width - popupMargin.right
} else {
anchorBounds.left + popupMargin.left
}
offsetY = if (windowBounds.bottom - anchorBounds.bottom > popupContentSize.height) {
anchorBounds.bottom + popupMargin.bottom
} else if (anchorBounds.top - windowBounds.top > popupContentSize.height) {
anchorBounds.top - popupContentSize.height - popupMargin.top
} else {
anchorBounds.top + anchorBounds.height / 2 - popupContentSize.height / 2
}
}
}
return IntOffset(
x = offsetX.coerceIn(
windowBounds.left,
(windowBounds.right - popupContentSize.width - popupMargin.right)
.coerceAtLeast(windowBounds.left),
),
y = offsetY.coerceIn(
(windowBounds.top + popupMargin.top)
.coerceAtMost(windowBounds.bottom - popupContentSize.height - popupMargin.bottom),
windowBounds.bottom - popupContentSize.height - popupMargin.bottom,
),
)
}
override fun getMargins(): PaddingValues = PaddingValues(start = 20.dp)
}
}
private fun PopupPositionProvider.Align.resolve(layoutDirection: LayoutDirection): PopupPositionProvider.Align {
if (layoutDirection == LayoutDirection.Ltr) return this
return when (this) {
PopupPositionProvider.Align.Start -> PopupPositionProvider.Align.End
PopupPositionProvider.Align.End -> PopupPositionProvider.Align.Start
PopupPositionProvider.Align.TopStart -> PopupPositionProvider.Align.TopEnd
PopupPositionProvider.Align.TopEnd -> PopupPositionProvider.Align.TopStart
PopupPositionProvider.Align.BottomStart -> PopupPositionProvider.Align.BottomEnd
PopupPositionProvider.Align.BottomEnd -> PopupPositionProvider.Align.BottomStart
}
}

View File

@@ -0,0 +1,103 @@
package com.topjohnwu.magisk.ui.component
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun SettingsArrow(
title: String,
summary: String? = null,
leadingContent: @Composable (() -> Unit)? = null,
onClick: () -> Unit
) {
ListItem(
headlineContent = { Text(title) },
supportingContent = summary?.let { { Text(it) } },
leadingContent = leadingContent,
trailingContent = { Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null) },
modifier = Modifier.clickable(onClick = onClick)
)
}
@Composable
fun SettingsSwitch(
title: String,
summary: String? = null,
checked: Boolean,
enabled: Boolean = true,
onCheckedChange: (Boolean) -> Unit
) {
ListItem(
headlineContent = { Text(title) },
supportingContent = summary?.takeIf { it.isNotEmpty() }?.let { { Text(it) } },
trailingContent = { Switch(checked = checked, onCheckedChange = onCheckedChange, enabled = enabled) },
modifier = Modifier.clickable(enabled = enabled, onClick = { onCheckedChange(!checked) })
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsDropdown(
title: String,
summary: String? = null,
items: List<String>,
selectedIndex: Int,
enabled: Boolean = true,
onSelectedIndexChange: (Int) -> Unit
) {
var expanded by remember { mutableStateOf(false) }
Box {
ListItem(
headlineContent = { Text(title) },
supportingContent = {
val currentSummary = summary ?: items.getOrNull(selectedIndex) ?: ""
if (currentSummary.isNotEmpty()) Text(currentSummary)
},
trailingContent = { Icon(Icons.Default.ArrowDropDown, contentDescription = null) },
modifier = Modifier.clickable(enabled = enabled, onClick = { expanded = true })
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
items.forEachIndexed { index, item ->
DropdownMenuItem(
text = { Text(item) },
onClick = {
onSelectedIndexChange(index)
expanded = false
}
)
}
}
}
}
@Composable
fun SmallTitle(text: String) {
Text(
text = text,
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
)
}

View File

@@ -1,5 +1,5 @@
package com.topjohnwu.magisk.ui.deny
import com.topjohnwu.magisk.ui.util.rememberDrawablePainter
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
@@ -28,29 +28,30 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.unit.dp
import com.topjohnwu.magisk.ui.component.ListPopupDefaults.MenuPositionProvider
import com.topjohnwu.magisk.ui.util.rememberDrawablePainter
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.Checkbox
import top.yukonga.miuix.kmp.basic.CircularProgressIndicator
import top.yukonga.miuix.kmp.basic.DropdownImpl
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.LinearProgressIndicator
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.extra.SuperListPopup
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Back
import top.yukonga.miuix.kmp.icon.extended.Sort
import top.yukonga.miuix.kmp.icon.extended.Tune
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Sort
import androidx.compose.material.icons.filled.Tune
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Card
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.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TriStateCheckbox
import com.topjohnwu.magisk.core.R as CoreR
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DenyListScreen(viewModel: DenyListViewModel, onBack: () -> Unit) {
val loading by viewModel.loading.collectAsState()
@@ -64,20 +65,20 @@ fun DenyListScreen(viewModel: DenyListViewModel, onBack: () -> Unit) {
val showSortMenu = remember { mutableStateOf(false) }
val showFilterMenu = remember { mutableStateOf(false) }
val scrollBehavior = MiuixScrollBehavior()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
Scaffold(
topBar = {
TopAppBar(
title = stringResource(CoreR.string.denylist),
title = { Text(stringResource(CoreR.string.denylist)) },
navigationIcon = {
IconButton(
modifier = Modifier.padding(start = 16.dp),
onClick = onBack
) {
Icon(
imageVector = MiuixIcons.Back,
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null,
tint = MiuixTheme.colorScheme.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
}
},
@@ -85,50 +86,44 @@ fun DenyListScreen(viewModel: DenyListViewModel, onBack: () -> Unit) {
Box {
IconButton(
onClick = { showSortMenu.value = true },
holdDownState = showSortMenu.value,
) {
Icon(
imageVector = MiuixIcons.Sort,
imageVector = Icons.AutoMirrored.Filled.Sort,
contentDescription = stringResource(CoreR.string.menu_sort),
)
}
SuperListPopup(
show = showSortMenu,
popupPositionProvider = MenuPositionProvider,
alignment = PopupPositionProvider.Align.End,
DropdownMenu(
expanded = showSortMenu.value,
onDismissRequest = { showSortMenu.value = false }
) {
ListPopupColumn {
val sortOptions = listOf(
CoreR.string.sort_by_name to SortBy.NAME,
CoreR.string.sort_by_package_name to SortBy.PACKAGE_NAME,
CoreR.string.sort_by_install_time to SortBy.INSTALL_TIME,
CoreR.string.sort_by_update_time to SortBy.UPDATE_TIME,
)
val totalSize = sortOptions.size + 1
sortOptions.forEachIndexed { index, (resId, sort) ->
DropdownImpl(
text = stringResource(resId),
optionSize = totalSize,
isSelected = sortBy == sort,
index = index,
onSelectedIndexChange = {
viewModel.setSortBy(sort)
showSortMenu.value = false
}
)
}
DropdownImpl(
text = stringResource(CoreR.string.sort_reverse),
optionSize = totalSize,
isSelected = sortReverse,
index = sortOptions.size,
onSelectedIndexChange = {
viewModel.toggleSortReverse()
val sortOptions = listOf(
CoreR.string.sort_by_name to SortBy.NAME,
CoreR.string.sort_by_package_name to SortBy.PACKAGE_NAME,
CoreR.string.sort_by_install_time to SortBy.INSTALL_TIME,
CoreR.string.sort_by_update_time to SortBy.UPDATE_TIME,
)
sortOptions.forEach { (resId, sort) ->
DropdownMenuItem(
text = { Text(stringResource(resId)) },
trailingIcon = if (sortBy == sort) {
{ Icon(androidx.compose.material.icons.Icons.Default.Check, contentDescription = null) }
} else null,
onClick = {
viewModel.setSortBy(sort)
showSortMenu.value = false
}
)
}
DropdownMenuItem(
text = { Text(stringResource(CoreR.string.sort_reverse)) },
trailingIcon = if (sortReverse) {
{ Icon(Icons.Default.Check, contentDescription = null) }
} else null,
onClick = {
viewModel.toggleSortReverse()
showSortMenu.value = false
}
)
}
}
@@ -136,51 +131,45 @@ fun DenyListScreen(viewModel: DenyListViewModel, onBack: () -> Unit) {
IconButton(
modifier = Modifier.padding(end = 16.dp),
onClick = { showFilterMenu.value = true },
holdDownState = showFilterMenu.value,
) {
Icon(
imageVector = MiuixIcons.Tune,
imageVector = Icons.Default.Tune,
contentDescription = stringResource(CoreR.string.hide_filter_hint),
)
}
SuperListPopup(
show = showFilterMenu,
popupPositionProvider = MenuPositionProvider,
alignment = PopupPositionProvider.Align.End,
DropdownMenu(
expanded = showFilterMenu.value,
onDismissRequest = { showFilterMenu.value = false }
) {
ListPopupColumn {
DropdownImpl(
text = stringResource(CoreR.string.show_system_app),
optionSize = 2,
isSelected = showSystem,
index = 0,
onSelectedIndexChange = {
viewModel.setShowSystem(!showSystem)
showFilterMenu.value = false
DropdownMenuItem(
text = { Text(stringResource(CoreR.string.show_system_app)) },
trailingIcon = if (showSystem) {
{ Icon(Icons.Default.Check, contentDescription = null) }
} else null,
onClick = {
viewModel.setShowSystem(!showSystem)
showFilterMenu.value = false
}
)
DropdownMenuItem(
text = { Text(stringResource(CoreR.string.show_os_app)) },
trailingIcon = if (showOS) {
{ Icon(Icons.Default.Check, contentDescription = null) }
} else null,
onClick = {
if (!showOS && !showSystem) {
viewModel.setShowSystem(true)
}
)
DropdownImpl(
text = stringResource(CoreR.string.show_os_app),
optionSize = 2,
isSelected = showOS,
index = 1,
onSelectedIndexChange = {
if (!showOS && !showSystem) {
viewModel.setShowSystem(true)
}
viewModel.setShowOS(!showOS)
showFilterMenu.value = false
}
)
}
viewModel.setShowOS(!showOS)
showFilterMenu.value = false
}
)
}
}
},
scrollBehavior = scrollBehavior
)
},
popupHost = { }
}
) { padding ->
Column(modifier = Modifier.fillMaxSize().padding(padding)) {
SearchInput(
@@ -199,7 +188,7 @@ fun DenyListScreen(viewModel: DenyListViewModel, onBack: () -> Unit) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = stringResource(CoreR.string.loading),
style = MiuixTheme.textStyles.headline2
style = MaterialTheme.typography.titleLarge
)
Spacer(Modifier.height(16.dp))
CircularProgressIndicator()
@@ -228,11 +217,11 @@ fun DenyListScreen(viewModel: DenyListViewModel, onBack: () -> Unit) {
@Composable
private fun SearchInput(query: String, onQueryChange: (String) -> Unit, modifier: Modifier = Modifier) {
top.yukonga.miuix.kmp.basic.TextField(
OutlinedTextField(
value = query,
onValueChange = onQueryChange,
modifier = modifier,
label = stringResource(CoreR.string.hide_filter_hint)
label = { Text(stringResource(CoreR.string.hide_filter_hint)) }
)
}
@@ -263,16 +252,16 @@ private fun DenyAppCard(app: DenyAppState) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = app.info.label,
style = MiuixTheme.textStyles.body1,
style = MaterialTheme.typography.bodyLarge,
)
Text(
text = app.info.packageName,
style = MiuixTheme.textStyles.body2,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Spacer(Modifier.width(8.dp))
Checkbox(
TriStateCheckbox(
state = when {
app.itemsChecked == 0 -> ToggleableState.Off
app.checkedPercent < 1f -> ToggleableState.Indeterminate
@@ -308,15 +297,15 @@ private fun ProcessRow(proc: DenyProcessState) {
) {
Text(
text = proc.displayName,
style = MiuixTheme.textStyles.body2,
color = if (proc.isEnabled) MiuixTheme.colorScheme.onSurface
else MiuixTheme.colorScheme.onSurfaceVariantSummary,
style = MaterialTheme.typography.bodyMedium,
color = if (proc.isEnabled) MaterialTheme.colorScheme.onSurface
else MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.weight(1f)
)
Spacer(Modifier.width(8.dp))
Checkbox(
state = ToggleableState(proc.isEnabled),
onClick = { proc.toggle() }
checked = proc.isEnabled,
onCheckedChange = { proc.toggle() }
)
}
}

View File

@@ -21,17 +21,20 @@ import androidx.compose.ui.unit.sp
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.ui.terminal.TerminalScreen
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.SmallTopAppBar
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Back
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
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 com.topjohnwu.magisk.core.R as CoreR
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FlashScreen(viewModel: FlashViewModel, action: String, onBack: () -> Unit) {
val flashState by viewModel.flashState.collectAsState()
@@ -45,20 +48,20 @@ fun FlashScreen(viewModel: FlashViewModel, action: String, onBack: () -> Unit) {
FlashViewModel.State.FAILED -> stringResource(CoreR.string.failure)
}
val scrollBehavior = MiuixScrollBehavior()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
Scaffold(
topBar = {
SmallTopAppBar(
title = "${stringResource(CoreR.string.flash_screen_title)} - $statusText",
TopAppBar(
title = { Text("${stringResource(CoreR.string.flash_screen_title)} - $statusText") },
navigationIcon = {
IconButton(
modifier = Modifier.padding(start = 16.dp),
onClick = onBack
) {
Icon(
imageVector = MiuixIcons.Back,
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null,
tint = MiuixTheme.colorScheme.onBackground
tint = MaterialTheme.colorScheme.onSurface
)
}
},
@@ -71,7 +74,7 @@ fun FlashScreen(viewModel: FlashViewModel, action: String, onBack: () -> Unit) {
Icon(
painter = painterResource(R.drawable.ic_save_md2),
contentDescription = stringResource(CoreR.string.menuSaveLog),
tint = MiuixTheme.colorScheme.onBackground
tint = MaterialTheme.colorScheme.onSurface
)
}
}
@@ -83,15 +86,14 @@ fun FlashScreen(viewModel: FlashViewModel, action: String, onBack: () -> Unit) {
Icon(
painter = painterResource(R.drawable.ic_restart),
contentDescription = stringResource(CoreR.string.reboot),
tint = MiuixTheme.colorScheme.onBackground
tint = MaterialTheme.colorScheme.onSurface
)
}
}
},
scrollBehavior = scrollBehavior
)
},
popupHost = { }
}
) { padding ->
if (useTerminal) {
TerminalScreen(
@@ -124,7 +126,7 @@ fun FlashScreen(viewModel: FlashViewModel, action: String, onBack: () -> Unit) {
fontFamily = FontFamily.Monospace,
fontSize = 12.sp,
lineHeight = 16.sp,
color = MiuixTheme.colorScheme.onSurface,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.fillMaxWidth()
)
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -45,21 +45,24 @@ import com.topjohnwu.magisk.core.ktx.timeDateFormat
import com.topjohnwu.magisk.core.ktx.toTime
import com.topjohnwu.magisk.core.model.su.SuLog
import com.topjohnwu.magisk.ui.util.rememberDrawablePainter
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.CircularProgressIndicator
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.TabRow
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Delete
import top.yukonga.miuix.kmp.icon.extended.Download
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Download
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
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.Tab
import androidx.compose.material3.TabRow
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)
@Composable
fun LogScreen(viewModel: LogViewModel) {
val uiState by viewModel.uiState.collectAsState()
@@ -68,17 +71,17 @@ fun LogScreen(viewModel: LogViewModel) {
stringResource(CoreR.string.superuser),
stringResource(CoreR.string.magisk)
)
val scrollBehavior = MiuixScrollBehavior()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
Scaffold(
topBar = {
TopAppBar(
title = stringResource(CoreR.string.logs),
title = { Text(stringResource(CoreR.string.logs)) },
actions = {
if (selectedTab == 1) {
IconButton(onClick = { viewModel.saveMagiskLog() }) {
Icon(
imageVector = MiuixIcons.Download,
imageVector = Icons.Default.Download,
contentDescription = stringResource(CoreR.string.save_log),
)
}
@@ -91,28 +94,33 @@ fun LogScreen(viewModel: LogViewModel) {
}
) {
Icon(
imageVector = MiuixIcons.Delete,
imageVector = Icons.Default.Delete,
contentDescription = stringResource(CoreR.string.clear_log),
)
}
},
scrollBehavior = scrollBehavior
)
},
popupHost = { }
}
) { padding ->
Column(modifier = Modifier
.fillMaxSize()
.padding(padding)
) {
TabRow(
tabs = tabTitles,
selectedTabIndex = selectedTab,
onTabSelected = { selectedTab = it },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp)
)
) {
tabTitles.forEachIndexed { index, title ->
Tab(
selected = selectedTab == index,
onClick = { selectedTab = index },
text = { Text(title) }
)
}
}
if (uiState.loading) {
Box(
@@ -150,8 +158,8 @@ private fun SuLogTab(logs: List<SuLog>, nestedScrollConnection: NestedScrollConn
) {
Text(
text = stringResource(CoreR.string.log_data_none),
style = MiuixTheme.textStyles.body1,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center,
)
}
@@ -220,14 +228,14 @@ private fun SuLogCard(log: SuLog) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = log.appName,
style = MiuixTheme.textStyles.body1,
style = MaterialTheme.typography.bodyLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
text = uidPidText,
style = MiuixTheme.textStyles.body2,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
@@ -238,7 +246,7 @@ private fun SuLogCard(log: SuLog) {
text = log.time.toTime(timeDateFormat),
fontSize = 11.sp,
fontFamily = FontFamily.Monospace,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
)
Spacer(Modifier.height(4.dp))
@@ -253,7 +261,7 @@ private fun SuLogCard(log: SuLog) {
fontFamily = FontFamily.Monospace,
fontSize = 12.sp,
lineHeight = 16.sp,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
@@ -262,8 +270,8 @@ private fun SuLogCard(log: SuLog) {
@Composable
private fun SuActionBadge(allowed: Boolean) {
val bg = if (allowed) MiuixTheme.colorScheme.primary else MiuixTheme.colorScheme.error
val fg = if (allowed) MiuixTheme.colorScheme.onPrimary else MiuixTheme.colorScheme.onError
val bg = if (allowed) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error
val fg = if (allowed) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onError
val text = if (allowed) "Approved" else "Rejected"
Text(
text = text,
@@ -292,8 +300,8 @@ private fun MagiskLogTab(
) {
Text(
text = stringResource(CoreR.string.log_data_magisk_none),
style = MiuixTheme.textStyles.body1,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center,
)
}
@@ -340,7 +348,7 @@ private fun MagiskLogCard(entry: MagiskLogEntry) {
LogLevelBadge(entry.level)
Text(
text = entry.tag,
style = MiuixTheme.textStyles.body1,
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.Normal,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@@ -351,7 +359,7 @@ private fun MagiskLogCard(entry: MagiskLogEntry) {
text = entry.timestamp,
fontSize = 11.sp,
fontFamily = FontFamily.Monospace,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
)
}
@@ -363,7 +371,7 @@ private fun MagiskLogCard(entry: MagiskLogEntry) {
fontFamily = FontFamily.Monospace,
fontSize = 12.sp,
lineHeight = 16.sp,
color = MiuixTheme.colorScheme.onSurface,
color = MaterialTheme.colorScheme.onSurface,
maxLines = if (expanded) Int.MAX_VALUE else 3,
overflow = TextOverflow.Ellipsis,
)

View File

@@ -11,35 +11,37 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.ui.terminal.TerminalScreen
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.SmallTopAppBar
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Back
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
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.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import com.topjohnwu.magisk.core.R as CoreR
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ActionScreen(viewModel: ActionViewModel, actionName: String, onBack: () -> Unit) {
val actionState by viewModel.actionState.collectAsState()
val finished = actionState != ActionViewModel.State.RUNNING
val scrollBehavior = MiuixScrollBehavior()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
Scaffold(
topBar = {
SmallTopAppBar(
title = actionName,
TopAppBar(
title = { androidx.compose.material3.Text(actionName) },
navigationIcon = {
IconButton(
modifier = Modifier.padding(start = 16.dp),
onClick = onBack
) {
Icon(
imageVector = MiuixIcons.Back,
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null,
tint = MiuixTheme.colorScheme.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
}
},
@@ -52,15 +54,14 @@ fun ActionScreen(viewModel: ActionViewModel, actionName: String, onBack: () -> U
Icon(
painter = painterResource(R.drawable.ic_save_md2),
contentDescription = stringResource(CoreR.string.menuSaveLog),
tint = MiuixTheme.colorScheme.onBackground
tint = MaterialTheme.colorScheme.onBackground
)
}
}
},
scrollBehavior = scrollBehavior
)
},
popupHost = { }
}
) { padding ->
TerminalScreen(
modifier = Modifier

View File

@@ -9,6 +9,7 @@ import androidx.compose.animation.animateContentSize
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -51,34 +52,36 @@ import com.topjohnwu.magisk.ui.component.ConfirmResult
import com.topjohnwu.magisk.ui.component.MarkdownTextAsync
import com.topjohnwu.magisk.ui.component.rememberConfirmDialog
import kotlinx.coroutines.launch
import top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.CircularProgressIndicator
import top.yukonga.miuix.kmp.basic.FloatingActionButton
import top.yukonga.miuix.kmp.basic.HorizontalDivider
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.Switch
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TextButton
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.extra.SuperDialog
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Add
import top.yukonga.miuix.kmp.icon.extended.Delete
import top.yukonga.miuix.kmp.icon.extended.Play
import top.yukonga.miuix.kmp.icon.extended.Undo
import top.yukonga.miuix.kmp.icon.extended.UploadCloud
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.CloudUpload
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Undo
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import com.topjohnwu.magisk.core.R as CoreR
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ModuleScreen(viewModel: ModuleViewModel) {
val uiState by viewModel.uiState.collectAsState()
val scrollBehavior = MiuixScrollBehavior()
val colorScheme = MiuixTheme.colorScheme
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val colorScheme = MaterialTheme.colorScheme
val context = LocalContext.current
val scope = rememberCoroutineScope()
val activity = context as MainActivity
@@ -134,28 +137,27 @@ fun ModuleScreen(viewModel: ModuleViewModel) {
Scaffold(
topBar = {
TopAppBar(
title = stringResource(CoreR.string.modules),
title = { Text(stringResource(CoreR.string.modules)) },
scrollBehavior = scrollBehavior
)
},
floatingActionButton = {
FloatingActionButton(
onClick = { filePicker.launch("application/zip") },
shadowElevation = 0.dp,
shape = CircleShape,
modifier = Modifier
.padding(bottom = 88.dp, end = 20.dp)
.border(0.05.dp, colorScheme.outline.copy(alpha = 0.5f), CircleShape),
content = {
Icon(
imageVector = MiuixIcons.Add,
imageVector = Icons.Default.Add,
contentDescription = stringResource(CoreR.string.module_action_install_external),
modifier = Modifier.size(28.dp),
tint = colorScheme.onPrimary
tint = colorScheme.onPrimaryContainer
)
},
)
},
popupHost = { }
}
) { padding ->
if (uiState.loading) {
Box(
@@ -178,8 +180,8 @@ fun ModuleScreen(viewModel: ModuleViewModel) {
) {
Text(
text = stringResource(CoreR.string.module_empty),
style = MiuixTheme.textStyles.body1,
color = colorScheme.onSurfaceVariantSummary
style = MaterialTheme.typography.bodyLarge,
color = colorScheme.onSurfaceVariant
)
}
return@Scaffold
@@ -216,7 +218,7 @@ fun ModuleScreen(viewModel: ModuleViewModel) {
private fun ModuleCard(item: ModuleItem, viewModel: ModuleViewModel, onUpdateClick: (OnlineModule?) -> Unit) {
val infoAlpha = if (!item.isRemoved && item.isEnabled && !item.showNotice) 1f else 0.5f
val strikeThrough = if (item.isRemoved) TextDecoration.LineThrough else TextDecoration.None
val colorScheme = MiuixTheme.colorScheme
val colorScheme = MaterialTheme.colorScheme
val actionIconTint = colorScheme.onSurface.copy(alpha = 0.8f)
val actionBg = colorScheme.secondaryContainer.copy(alpha = 0.8f)
val updateBg = colorScheme.tertiaryContainer.copy(alpha = 0.6f)
@@ -227,16 +229,15 @@ private fun ModuleCard(item: ModuleItem, viewModel: ModuleViewModel, onUpdateCli
val hasDescription = item.module.description.isNotBlank()
Card(
modifier = Modifier.fillMaxWidth(),
insideMargin = PaddingValues(16.dp),
onClick = { if (hasDescription) expanded = !expanded }
modifier = Modifier.fillMaxWidth().clickable(enabled = hasDescription) { expanded = !expanded },
) {
Column(modifier = Modifier.alpha(infoAlpha)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.padding(16.dp)) {
Column(modifier = Modifier.alpha(infoAlpha)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.weight(1f)
@@ -244,7 +245,7 @@ private fun ModuleCard(item: ModuleItem, viewModel: ModuleViewModel, onUpdateCli
) {
Text(
text = item.module.name,
style = MiuixTheme.textStyles.body1,
style = MaterialTheme.typography.bodyLarge,
textDecoration = strikeThrough,
)
Text(
@@ -253,8 +254,8 @@ private fun ModuleCard(item: ModuleItem, viewModel: ModuleViewModel, onUpdateCli
item.module.version,
item.module.author
),
style = MiuixTheme.textStyles.body2,
color = colorScheme.onSurfaceVariantSummary,
style = MaterialTheme.typography.bodyMedium,
color = colorScheme.onSurfaceVariant,
textDecoration = strikeThrough,
)
}
@@ -272,8 +273,8 @@ private fun ModuleCard(item: ModuleItem, viewModel: ModuleViewModel, onUpdateCli
) {
Text(
text = item.module.description,
style = MiuixTheme.textStyles.body2,
color = colorScheme.onSurfaceVariantSummary,
style = MaterialTheme.typography.bodyMedium,
color = colorScheme.onSurfaceVariant,
textDecoration = strikeThrough,
overflow = if (expanded) TextOverflow.Clip else TextOverflow.Ellipsis,
maxLines = if (expanded) Int.MAX_VALUE else 4,
@@ -285,7 +286,7 @@ private fun ModuleCard(item: ModuleItem, viewModel: ModuleViewModel, onUpdateCli
Spacer(Modifier.height(4.dp))
Text(
text = item.noticeText,
style = MiuixTheme.textStyles.body2,
style = MaterialTheme.typography.bodyMedium,
color = colorScheme.primary,
)
}
@@ -305,27 +306,25 @@ private fun ModuleCard(item: ModuleItem, viewModel: ModuleViewModel, onUpdateCli
) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
if (item.showAction) {
IconButton(
backgroundColor = actionBg,
minHeight = 35.dp,
minWidth = 35.dp,
Button(
colors = ButtonDefaults.buttonColors(containerColor = actionBg),
contentPadding = PaddingValues(horizontal = 10.dp),
onClick = { viewModel.runAction(item.module.id, item.module.name) },
) {
Row(
modifier = Modifier.padding(horizontal = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Icon(
modifier = Modifier.size(20.dp),
imageVector = MiuixIcons.Play,
imageVector = Icons.Default.PlayArrow,
tint = actionIconTint,
contentDescription = stringResource(CoreR.string.module_action)
)
Text(
text = stringResource(CoreR.string.module_action),
color = actionIconTint,
style = MiuixTheme.textStyles.body2,
style = MaterialTheme.typography.bodyMedium,
)
}
}
@@ -340,49 +339,45 @@ private fun ModuleCard(item: ModuleItem, viewModel: ModuleViewModel, onUpdateCli
enter = fadeIn(),
exit = fadeOut()
) {
IconButton(
Button(
modifier = Modifier.padding(end = 8.dp),
backgroundColor = updateBg,
minHeight = 35.dp,
minWidth = 35.dp,
colors = ButtonDefaults.buttonColors(containerColor = updateBg),
contentPadding = PaddingValues(horizontal = 10.dp),
onClick = { onUpdateClick(item.module.updateInfo) },
) {
Row(
modifier = Modifier.padding(horizontal = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Icon(
modifier = Modifier.size(20.dp),
imageVector = MiuixIcons.UploadCloud,
imageVector = Icons.Default.CloudUpload,
tint = updateTint,
contentDescription = stringResource(CoreR.string.update),
)
Text(
text = stringResource(CoreR.string.update),
color = updateTint,
style = MiuixTheme.textStyles.body2,
style = MaterialTheme.typography.bodyMedium,
)
}
}
}
IconButton(
backgroundColor = if (item.isRemoved) actionBg else removeBg,
minHeight = 35.dp,
minWidth = 35.dp,
Button(
colors = ButtonDefaults.buttonColors(containerColor = if (item.isRemoved) actionBg else removeBg),
contentPadding = PaddingValues(horizontal = 10.dp),
onClick = { viewModel.toggleRemove(item) },
enabled = !item.isUpdated
) {
val tint = if (item.isRemoved) actionIconTint else removeTint
Row(
modifier = Modifier.padding(horizontal = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Icon(
modifier = Modifier.size(20.dp),
imageVector = if (item.isRemoved) MiuixIcons.Undo else MiuixIcons.Delete,
imageVector = if (item.isRemoved) Icons.Default.Undo else Icons.Default.Delete,
tint = tint,
contentDescription = null
)
@@ -392,10 +387,11 @@ private fun ModuleCard(item: ModuleItem, viewModel: ModuleViewModel, onUpdateCli
else CoreR.string.module_state_remove
),
color = tint,
style = MiuixTheme.textStyles.body2,
style = MaterialTheme.typography.bodyMedium,
)
}
}
}
}
}
}
@@ -413,36 +409,34 @@ private fun OnlineModuleDialog(
item.name, item.version, item.versionCode
)
SuperDialog(
show = showDialog,
title = title,
onDismissRequest = onDismiss,
) {
MarkdownTextAsync {
val str = svc.fetchString(item.changelog)
if (str.length > 1000) str.substring(0, 1000) else str
}
Spacer(Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically
) {
TextButton(
text = stringResource(android.R.string.cancel),
onClick = onDismiss,
)
Spacer(Modifier.weight(1f))
TextButton(
text = stringResource(CoreR.string.download),
onClick = { onDownload(false) },
)
Spacer(Modifier.width(8.dp))
TextButton(
text = stringResource(CoreR.string.install),
onClick = { onDownload(true) },
colors = ButtonDefaults.textButtonColorsPrimary()
)
}
if (showDialog.value) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(title) },
text = {
MarkdownTextAsync {
val str = svc.fetchString(item.changelog)
if (str.length > 1000) str.substring(0, 1000) else str
}
},
confirmButton = {
TextButton(
onClick = { onDownload(true) }
) {
Text(stringResource(CoreR.string.install))
}
},
dismissButton = {
Row {
TextButton(onClick = onDismiss) {
Text(stringResource(android.R.string.cancel))
}
Spacer(Modifier.weight(1f))
TextButton(onClick = { onDownload(false) }) {
Text(stringResource(CoreR.string.download))
}
}
}
)
}
}

View File

@@ -36,32 +36,34 @@ import com.topjohnwu.magisk.core.tasks.AppMigration
import com.topjohnwu.magisk.core.utils.LocaleSetting
import com.topjohnwu.magisk.core.utils.MediaStoreUtils
import com.topjohnwu.magisk.ui.theme.ThemeState
import top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.SmallTitle
import top.yukonga.miuix.kmp.basic.TextButton
import top.yukonga.miuix.kmp.basic.TextField
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.extra.SuperArrow
import top.yukonga.miuix.kmp.extra.SuperDialog
import top.yukonga.miuix.kmp.extra.SuperDropdown
import top.yukonga.miuix.kmp.extra.SuperSwitch
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
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.MaterialTheme
import com.topjohnwu.magisk.ui.component.SettingsArrow
import com.topjohnwu.magisk.ui.component.SettingsDropdown
import com.topjohnwu.magisk.ui.component.SettingsSwitch
import com.topjohnwu.magisk.ui.component.SmallTitle
import com.topjohnwu.magisk.core.R as CoreR
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(viewModel: SettingsViewModel) {
val scrollBehavior = MiuixScrollBehavior()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
Scaffold(
topBar = {
TopAppBar(
title = stringResource(CoreR.string.settings),
title = { Text(stringResource(CoreR.string.settings)) },
scrollBehavior = scrollBehavior
)
},
popupHost = { }
}
) { padding ->
Column(
modifier = Modifier
@@ -98,7 +100,7 @@ private fun CustomizationSection(viewModel: SettingsViewModel) {
if (LocaleSetting.useLocaleManager) {
val locale = LocaleSetting.instance.appLocale
val summary = locale?.getDisplayName(locale) ?: stringResource(CoreR.string.system_default)
SuperArrow(
SettingsArrow(
title = stringResource(CoreR.string.language),
summary = summary,
onClick = {
@@ -111,7 +113,7 @@ private fun CustomizationSection(viewModel: SettingsViewModel) {
var selectedIndex by remember {
mutableIntStateOf(tags.indexOf(Config.locale).coerceAtLeast(0))
}
SuperDropdown(
SettingsDropdown(
title = stringResource(CoreR.string.language),
items = names.toList(),
selectedIndex = selectedIndex,
@@ -128,7 +130,7 @@ private fun CustomizationSection(viewModel: SettingsViewModel) {
resources.getStringArray(CoreR.array.color_mode).toList()
}
var colorMode by remember { mutableIntStateOf(Config.colorMode) }
SuperDropdown(
SettingsDropdown(
title = stringResource(CoreR.string.settings_color_mode),
items = colorModeEntries,
selectedIndex = colorMode,
@@ -140,7 +142,7 @@ private fun CustomizationSection(viewModel: SettingsViewModel) {
)
if (isRunningAsStub && ShortcutManagerCompat.isRequestPinShortcutSupported(context)) {
SuperArrow(
SettingsArrow(
title = stringResource(CoreR.string.add_shortcut_title),
summary = stringResource(CoreR.string.setting_add_shortcut_summary),
onClick = { viewModel.requestAddShortcut() }
@@ -168,7 +170,7 @@ private fun AppSettingsSection(viewModel: SettingsViewModel) {
}
var showUrlDialog by remember { mutableStateOf(false) }
SuperDropdown(
SettingsDropdown(
title = stringResource(CoreR.string.settings_update_channel_title),
items = updateChannelEntries,
selectedIndex = updateChannel,
@@ -188,7 +190,7 @@ private fun AppSettingsSection(viewModel: SettingsViewModel) {
show = showUrlDialog,
onDismiss = { showUrlDialog = false }
)
SuperArrow(
SettingsArrow(
title = stringResource(CoreR.string.settings_update_custom),
summary = Config.customChannelUrl.ifBlank { null },
onClick = { showUrlDialog = true }
@@ -197,7 +199,7 @@ private fun AppSettingsSection(viewModel: SettingsViewModel) {
// DoH Toggle
var doh by remember { mutableStateOf(Config.doh) }
SuperSwitch(
SettingsSwitch(
title = stringResource(CoreR.string.settings_doh_title),
summary = stringResource(CoreR.string.settings_doh_description),
checked = doh,
@@ -209,7 +211,7 @@ private fun AppSettingsSection(viewModel: SettingsViewModel) {
// Update Checker
var checkUpdate by remember { mutableStateOf(Config.checkUpdate) }
SuperSwitch(
SettingsSwitch(
title = stringResource(CoreR.string.settings_check_update_title),
summary = stringResource(CoreR.string.settings_check_update_summary),
checked = checkUpdate,
@@ -225,7 +227,7 @@ private fun AppSettingsSection(viewModel: SettingsViewModel) {
show = showDownloadDialog,
onDismiss = { showDownloadDialog = false }
)
SuperArrow(
SettingsArrow(
title = stringResource(CoreR.string.settings_download_path_title),
summary = MediaStoreUtils.fullPath(Config.downloadDir),
onClick = {
@@ -235,7 +237,7 @@ private fun AppSettingsSection(viewModel: SettingsViewModel) {
// Random Package Name
var randName by remember { mutableStateOf(Config.randName) }
SuperSwitch(
SettingsSwitch(
title = stringResource(CoreR.string.settings_random_name_title),
summary = stringResource(CoreR.string.settings_random_name_description),
checked = randName,
@@ -255,7 +257,7 @@ private fun MagiskSection(viewModel: SettingsViewModel) {
SmallTitle(text = stringResource(CoreR.string.magisk))
Card(modifier = Modifier.fillMaxWidth()) {
// Systemless Hosts
SuperArrow(
SettingsArrow(
title = stringResource(CoreR.string.settings_hosts_title),
summary = stringResource(CoreR.string.settings_hosts_summary),
onClick = { viewModel.createHosts() }
@@ -264,7 +266,7 @@ private fun MagiskSection(viewModel: SettingsViewModel) {
if (Const.Version.atLeast_24_0()) {
// Zygisk
var zygisk by remember { mutableStateOf(Config.zygisk) }
SuperSwitch(
SettingsSwitch(
title = stringResource(CoreR.string.zygisk),
summary = stringResource(
if (zygisk != Info.isZygiskEnabled) CoreR.string.reboot_apply_change
@@ -280,7 +282,7 @@ private fun MagiskSection(viewModel: SettingsViewModel) {
// DenyList
val denyListEnabled by viewModel.denyListEnabled.collectAsState()
SuperSwitch(
SettingsSwitch(
title = stringResource(CoreR.string.settings_denylist_title),
summary = stringResource(CoreR.string.settings_denylist_summary),
checked = denyListEnabled,
@@ -288,7 +290,7 @@ private fun MagiskSection(viewModel: SettingsViewModel) {
)
// DenyList Config
SuperArrow(
SettingsArrow(
title = stringResource(CoreR.string.settings_denylist_config_title),
summary = stringResource(CoreR.string.settings_denylist_config_summary),
onClick = { viewModel.navigateToDenyList() }
@@ -309,7 +311,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
// Tapjack (SDK < S)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
var tapjack by remember { mutableStateOf(Config.suTapjack) }
SuperSwitch(
SettingsSwitch(
title = stringResource(CoreR.string.settings_su_tapjack_title),
summary = stringResource(CoreR.string.settings_su_tapjack_summary),
checked = tapjack,
@@ -322,7 +324,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
// Authentication
var suAuth by remember { mutableStateOf(Config.suAuth) }
SuperSwitch(
SettingsSwitch(
title = stringResource(CoreR.string.settings_su_auth_title),
summary = stringResource(
if (Info.isDeviceSecure) CoreR.string.settings_su_auth_summary
@@ -343,7 +345,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
resources.getStringArray(CoreR.array.su_access).toList()
}
var accessMode by remember { mutableIntStateOf(Config.rootMode) }
SuperDropdown(
SettingsDropdown(
title = stringResource(CoreR.string.superuser_access),
items = accessEntries,
selectedIndex = accessMode,
@@ -361,7 +363,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
resources.getStringArray(CoreR.array.multiuser_summary).toList()
}
var multiuserMode by remember { mutableIntStateOf(Config.suMultiuserMode) }
SuperDropdown(
SettingsDropdown(
title = stringResource(CoreR.string.multiuser_mode),
summary = multiuserDescriptions.getOrElse(multiuserMode) { "" },
items = multiuserEntries,
@@ -381,7 +383,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
resources.getStringArray(CoreR.array.namespace_summary).toList()
}
var mntNamespaceMode by remember { mutableIntStateOf(Config.suMntNamespaceMode) }
SuperDropdown(
SettingsDropdown(
title = stringResource(CoreR.string.mount_namespace_mode),
summary = namespaceDescriptions.getOrElse(mntNamespaceMode) { "" },
items = namespaceEntries,
@@ -397,7 +399,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
resources.getStringArray(CoreR.array.auto_response).toList()
}
var autoResponse by remember { mutableIntStateOf(Config.suAutoResponse) }
SuperDropdown(
SettingsDropdown(
title = stringResource(CoreR.string.auto_response),
items = autoResponseEntries,
selectedIndex = autoResponse,
@@ -418,7 +420,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
var timeoutIndex by remember {
mutableIntStateOf(timeoutValues.indexOf(Config.suDefaultTimeout).coerceAtLeast(0))
}
SuperDropdown(
SettingsDropdown(
title = stringResource(CoreR.string.request_timeout),
items = timeoutEntries,
selectedIndex = timeoutIndex,
@@ -433,7 +435,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
resources.getStringArray(CoreR.array.su_notification).toList()
}
var suNotification by remember { mutableIntStateOf(Config.suNotification) }
SuperDropdown(
SettingsDropdown(
title = stringResource(CoreR.string.superuser_notification),
items = notifEntries,
selectedIndex = suNotification,
@@ -446,7 +448,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
// Reauthenticate (SDK < O)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
var reAuth by remember { mutableStateOf(Config.suReAuth) }
SuperSwitch(
SettingsSwitch(
title = stringResource(CoreR.string.settings_su_reauth_title),
summary = stringResource(CoreR.string.settings_su_reauth_summary),
checked = reAuth,
@@ -460,7 +462,7 @@ private fun SuperuserSection(viewModel: SettingsViewModel) {
// Restrict (version >= 30.1)
if (Const.Version.atLeast_30_1()) {
var restrict by remember { mutableStateOf(Config.suRestrict) }
SuperSwitch(
SettingsSwitch(
title = stringResource(CoreR.string.settings_su_restrict_title),
summary = stringResource(CoreR.string.settings_su_restrict_summary),
checked = restrict,
@@ -481,29 +483,29 @@ private fun UpdateChannelUrlDialog(show: Boolean, onDismiss: () -> Unit) {
showState.value = show
var url by rememberSaveable { mutableStateOf(Config.customChannelUrl) }
SuperDialog(
show = showState,
onDismissRequest = onDismiss,
insideMargin = DpSize(24.dp, 24.dp)
) {
Column(modifier = Modifier.padding(top = 8.dp)) {
TextField(
value = url,
onValueChange = { url = it },
modifier = Modifier.fillMaxWidth(),
label = stringResource(CoreR.string.settings_update_custom_msg)
)
Spacer(Modifier.height(16.dp))
TextButton(
text = stringResource(android.R.string.ok),
onClick = {
Config.customChannelUrl = url
Info.resetUpdate()
onDismiss()
},
modifier = Modifier.fillMaxWidth()
)
}
if (showState.value) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(stringResource(CoreR.string.settings_update_custom_msg)) },
text = {
OutlinedTextField(
value = url,
onValueChange = { url = it },
modifier = Modifier.fillMaxWidth()
)
},
confirmButton = {
TextButton(
onClick = {
Config.customChannelUrl = url
Info.resetUpdate()
onDismiss()
}
) {
Text(stringResource(android.R.string.ok))
}
}
)
}
}
@@ -513,32 +515,34 @@ private fun DownloadPathDialog(show: Boolean, onDismiss: () -> Unit) {
showState.value = show
var path by rememberSaveable { mutableStateOf(Config.downloadDir) }
SuperDialog(
show = showState,
onDismissRequest = onDismiss,
insideMargin = DpSize(24.dp, 24.dp)
) {
Column(modifier = Modifier.padding(top = 8.dp)) {
top.yukonga.miuix.kmp.basic.Text(
text = stringResource(CoreR.string.settings_download_path_message, MediaStoreUtils.fullPath(path)),
modifier = Modifier.padding(bottom = 8.dp)
)
TextField(
value = path,
onValueChange = { path = it },
modifier = Modifier.fillMaxWidth(),
label = stringResource(CoreR.string.settings_download_path_title)
)
Spacer(Modifier.height(16.dp))
TextButton(
text = stringResource(android.R.string.ok),
onClick = {
Config.downloadDir = path
onDismiss()
},
modifier = Modifier.fillMaxWidth()
)
}
if (showState.value) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(stringResource(CoreR.string.settings_download_path_title)) },
text = {
Column {
Text(
text = stringResource(CoreR.string.settings_download_path_message, MediaStoreUtils.fullPath(path)),
modifier = Modifier.padding(bottom = 8.dp)
)
OutlinedTextField(
value = path,
onValueChange = { path = it },
modifier = Modifier.fillMaxWidth()
)
}
},
confirmButton = {
TextButton(
onClick = {
Config.downloadDir = path
onDismiss()
}
) {
Text(stringResource(android.R.string.ok))
}
}
)
}
}
@@ -549,37 +553,31 @@ private fun HideAppDialog(show: Boolean, onDismiss: () -> Unit, onConfirm: (Stri
var appName by rememberSaveable { mutableStateOf("Settings") }
val isError = appName.length > AppMigration.MAX_LABEL_LENGTH || appName.isBlank()
SuperDialog(
show = showState,
title = stringResource(CoreR.string.settings_hide_app_title),
onDismissRequest = onDismiss,
insideMargin = DpSize(24.dp, 24.dp)
) {
Column(modifier = Modifier.padding(top = 8.dp)) {
TextField(
value = appName,
onValueChange = { appName = it },
modifier = Modifier.fillMaxWidth(),
label = stringResource(CoreR.string.settings_app_name_hint),
)
Spacer(Modifier.height(16.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
) {
TextButton(
text = stringResource(android.R.string.cancel),
onClick = onDismiss,
modifier = Modifier.weight(1f)
if (showState.value) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(stringResource(CoreR.string.settings_hide_app_title)) },
text = {
OutlinedTextField(
value = appName,
onValueChange = { appName = it },
modifier = Modifier.fillMaxWidth(),
label = { Text(stringResource(CoreR.string.settings_app_name_hint)) }
)
Spacer(Modifier.width(20.dp))
},
confirmButton = {
TextButton(
text = stringResource(android.R.string.ok),
onClick = { if (!isError) onConfirm(appName) },
modifier = Modifier.weight(1f),
colors = ButtonDefaults.textButtonColorsPrimary()
)
onClick = { if (!isError) onConfirm(appName) }
) {
Text(stringResource(android.R.string.ok))
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(android.R.string.cancel))
}
}
}
)
}
}
@@ -588,34 +586,26 @@ private fun RestoreDialog(show: Boolean, onDismiss: () -> Unit, onConfirm: () ->
val showState = rememberSaveable { mutableStateOf(show) }
showState.value = show
SuperDialog(
show = showState,
title = stringResource(CoreR.string.settings_restore_app_title),
onDismissRequest = onDismiss,
insideMargin = DpSize(24.dp, 24.dp)
) {
Column(modifier = Modifier.padding(top = 8.dp)) {
top.yukonga.miuix.kmp.basic.Text(
text = stringResource(CoreR.string.restore_app_confirmation),
color = MiuixTheme.colorScheme.onSurface,
)
Spacer(Modifier.height(16.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
) {
TextButton(
text = stringResource(android.R.string.cancel),
onClick = onDismiss,
modifier = Modifier.weight(1f)
)
Spacer(Modifier.width(20.dp))
TextButton(
text = stringResource(android.R.string.ok),
onClick = onConfirm,
modifier = Modifier.weight(1f),
colors = ButtonDefaults.textButtonColorsPrimary()
if (showState.value) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(stringResource(CoreR.string.settings_restore_app_title)) },
text = {
Text(
text = stringResource(CoreR.string.restore_app_confirmation),
color = MaterialTheme.colorScheme.onSurface,
)
},
confirmButton = {
TextButton(onClick = onConfirm) {
Text(stringResource(android.R.string.ok))
}
},
dismissButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(android.R.string.cancel))
}
}
}
)
}
}

View File

@@ -28,19 +28,21 @@ import com.topjohnwu.magisk.ui.component.ConfirmResult
import com.topjohnwu.magisk.ui.component.rememberConfirmDialog
import com.topjohnwu.magisk.ui.util.rememberDrawablePainter
import kotlinx.coroutines.launch
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.Switch
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Back
import top.yukonga.miuix.kmp.theme.MiuixTheme
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)
@Composable
fun SuperuserDetailScreen(
uid: Int,
@@ -50,7 +52,7 @@ fun SuperuserDetailScreen(
val uiState by viewModel.uiState.collectAsState()
val items = uiState.policies.filter { it.policy.uid == uid }
val item = items.firstOrNull()
val scrollBehavior = MiuixScrollBehavior()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val scope = rememberCoroutineScope()
val revokeDialog = rememberConfirmDialog()
val revokeTitle = stringResource(CoreR.string.su_revoke_title)
@@ -61,22 +63,21 @@ fun SuperuserDetailScreen(
Scaffold(
topBar = {
TopAppBar(
title = stringResource(CoreR.string.superuser_setting),
title = { Text(stringResource(CoreR.string.superuser_setting)) },
navigationIcon = {
IconButton(
modifier = Modifier.padding(start = 16.dp),
onClick = onBack
) {
Icon(
imageVector = MiuixIcons.Back,
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(CoreR.string.back),
)
}
},
scrollBehavior = scrollBehavior
)
},
popupHost = { }
}
) { padding ->
if (item == null) return@Scaffold
@@ -109,7 +110,7 @@ fun SuperuserDetailScreen(
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = item.title,
style = MiuixTheme.textStyles.headline2,
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.weight(1f, fill = false),
)
if (item.isSharedUid) {
@@ -119,13 +120,13 @@ fun SuperuserDetailScreen(
}
Text(
text = item.packageName,
style = MiuixTheme.textStyles.body2,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = "UID: ${item.policy.uid}",
style = MiuixTheme.textStyles.body2,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@@ -197,7 +198,7 @@ private fun SwitchRow(
) {
Text(
text = title,
style = MiuixTheme.textStyles.body1,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.weight(1f),
)
Spacer(Modifier.width(16.dp))
@@ -212,8 +213,8 @@ private fun SwitchRow(
private fun RevokeRow() {
Text(
text = stringResource(CoreR.string.superuser_toggle_revoke),
style = MiuixTheme.textStyles.body1,
color = MiuixTheme.colorScheme.error,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.error,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 16.dp)

View File

@@ -33,30 +33,31 @@ 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 top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.CircularProgressIndicator
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.Switch
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material3.Card
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
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)
@Composable
fun SuperuserScreen(viewModel: SuperuserViewModel) {
val uiState by viewModel.uiState.collectAsState()
val scrollBehavior = MiuixScrollBehavior()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val navigator = LocalNavigator.current
Scaffold(
topBar = {
TopAppBar(
title = stringResource(CoreR.string.superuser),
title = { Text(stringResource(CoreR.string.superuser)) },
scrollBehavior = scrollBehavior
)
},
popupHost = { }
}
) { padding ->
if (uiState.loading) {
Box(
@@ -79,8 +80,8 @@ fun SuperuserScreen(viewModel: SuperuserViewModel) {
) {
Text(
text = stringResource(CoreR.string.superuser_policy_none),
style = MiuixTheme.textStyles.body1,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
return@Scaffold
@@ -142,7 +143,7 @@ private fun PolicyCard(
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = item.title,
style = MiuixTheme.textStyles.body1,
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.weight(1f, fill = false),
)
if (item.isSharedUid) {
@@ -152,8 +153,8 @@ private fun PolicyCard(
}
Text(
text = item.packageName,
style = MiuixTheme.textStyles.body2,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@@ -163,7 +164,7 @@ private fun PolicyCard(
.fillMaxHeight()
.padding(vertical = 12.dp)
.width(0.5.dp)
.background(MiuixTheme.colorScheme.dividerLine)
.background(MaterialTheme.colorScheme.outlineVariant)
)
Box(
@@ -185,11 +186,11 @@ private fun PolicyCard(
internal fun SharedUidBadge(modifier: Modifier = Modifier) {
Text(
text = "SharedUID",
color = MiuixTheme.colorScheme.onPrimary,
color = MaterialTheme.colorScheme.onPrimary,
fontSize = 10.sp,
maxLines = 1,
modifier = modifier
.background(MiuixTheme.colorScheme.primary, RoundedCornerShape(6.dp))
.background(MaterialTheme.colorScheme.primary, RoundedCornerShape(6.dp))
.padding(horizontal = 6.dp, vertical = 2.dp)
)
}

View File

@@ -34,7 +34,6 @@ import com.topjohnwu.magisk.ui.theme.Theme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.yukonga.miuix.kmp.utils.MiuixPopupUtils.Companion.MiuixPopupHost
open class SuRequestActivity : AppCompatActivity(), UntrackedActivity {
@@ -92,7 +91,6 @@ open class SuRequestActivity : AppCompatActivity(), UntrackedActivity {
MagiskTheme {
Box(modifier = Modifier.fillMaxSize()) {
SuRequestScreen(viewModel = viewModel)
MiuixPopupHost()
}
}
}

View File

@@ -34,13 +34,12 @@ import androidx.compose.ui.unit.dp
import com.topjohnwu.magisk.core.ktx.toast
import com.topjohnwu.magisk.ui.superuser.SharedUidBadge
import com.topjohnwu.magisk.ui.util.rememberDrawablePainter
import top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.Slider
import top.yukonga.miuix.kmp.basic.SliderDefaults
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TextButton
import top.yukonga.miuix.kmp.theme.MiuixTheme
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import com.topjohnwu.magisk.core.R as CoreR
@OptIn(ExperimentalComposeUiApi::class)
@@ -103,7 +102,7 @@ fun SuRequestScreen(viewModel: SuRequestViewModel) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = title,
style = MiuixTheme.textStyles.body1,
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.Bold,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@@ -116,8 +115,8 @@ fun SuRequestScreen(viewModel: SuRequestViewModel) {
}
Text(
text = packageName,
style = MiuixTheme.textStyles.body2,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
@@ -128,8 +127,8 @@ fun SuRequestScreen(viewModel: SuRequestViewModel) {
Text(
text = stringResource(CoreR.string.su_request_title),
style = MiuixTheme.textStyles.body2,
color = MiuixTheme.colorScheme.primary,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
)
@@ -138,8 +137,8 @@ fun SuRequestScreen(viewModel: SuRequestViewModel) {
Text(
text = "Permission timeout: $sliderLabel",
style = MiuixTheme.textStyles.body2,
color = MiuixTheme.colorScheme.onSurfaceVariantSummary,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.fillMaxWidth().padding(start = 8.dp),
)
Spacer(Modifier.height(8.dp))
@@ -152,9 +151,6 @@ fun SuRequestScreen(viewModel: SuRequestViewModel) {
},
valueRange = 0f..5f,
steps = 4,
showKeyPoints = true,
height = 20.dp,
hapticEffect = SliderDefaults.SliderHapticEffect.Step,
modifier = Modifier.fillMaxWidth().padding(horizontal = 4.dp)
)
Spacer(Modifier.height(16.dp))
@@ -163,22 +159,15 @@ fun SuRequestScreen(viewModel: SuRequestViewModel) {
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TextButton(
text = denyText,
OutlinedButton(
onClick = { viewModel.denyPressed() },
modifier = Modifier.weight(1f),
cornerRadius = 12.dp,
minHeight = 40.dp,
insideMargin = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
)
TextButton(
text = stringResource(CoreR.string.grant),
) {
Text(denyText)
}
Button(
enabled = grantEnabled,
colors = ButtonDefaults.textButtonColorsPrimary(),
onClick = { viewModel.grantPressed() },
cornerRadius = 12.dp,
minHeight = 40.dp,
insideMargin = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
modifier = Modifier
.weight(1f)
.then(
@@ -200,7 +189,9 @@ fun SuRequestScreen(viewModel: SuRequestViewModel) {
}
} else Modifier
)
)
) {
Text(stringResource(CoreR.string.grant))
}
}
}
}

View File

@@ -1,16 +1,18 @@
package com.topjohnwu.magisk.ui.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import com.topjohnwu.magisk.core.Config
import top.yukonga.miuix.kmp.theme.ColorSchemeMode
import top.yukonga.miuix.kmp.theme.LocalContentColor
import top.yukonga.miuix.kmp.theme.MiuixTheme
import top.yukonga.miuix.kmp.theme.ThemeController
object ThemeState {
var colorMode by mutableIntStateOf(Config.colorMode)
@@ -22,18 +24,28 @@ fun MagiskTheme(
) {
val isDark = isSystemInDarkTheme()
val mode = ThemeState.colorMode
val controller = when (mode) {
1 -> ThemeController(ColorSchemeMode.Light)
2 -> ThemeController(ColorSchemeMode.Dark)
3 -> ThemeController(ColorSchemeMode.MonetSystem, isDark = isDark)
4 -> ThemeController(ColorSchemeMode.MonetLight)
5 -> ThemeController(ColorSchemeMode.MonetDark)
else -> ThemeController(ColorSchemeMode.System)
val context = LocalContext.current
val isDarkTheme = when (mode) {
1 -> false
2 -> true
3 -> isDark
4 -> false
5 -> true
else -> isDark
}
MiuixTheme(controller = controller) {
CompositionLocalProvider(
LocalContentColor provides MiuixTheme.colorScheme.onBackground,
content = content
)
val useDynamicColor = mode in listOf(3, 4, 5) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val colorScheme = when {
useDynamicColor && isDarkTheme -> dynamicDarkColorScheme(context)
useDynamicColor && !isDarkTheme -> dynamicLightColorScheme(context)
isDarkTheme -> darkColorScheme()
else -> lightColorScheme()
}
MaterialTheme(
colorScheme = colorScheme,
content = content
)
}

View File

@@ -66,6 +66,8 @@ rikka-insets = { module = "dev.rikka.rikkax.insets:insets", version.ref = "rikka
activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" }
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" }
compose-ui = { module = "androidx.compose.ui:ui" }
compose-material3 = { module = "androidx.compose.material3:material3" }
compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" }
@@ -74,6 +76,7 @@ miuix = { module = "top.yukonga.miuix.kmp:miuix-android", version.ref = "miuix"
miuix-icons = { module = "top.yukonga.miuix.kmp:miuix-icons-android", version.ref = "miuix" }
miuix-navigation3-ui = { module = "top.yukonga.miuix.kmp:miuix-navigation3-ui-android", version.ref = "miuix" }
navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "navigation3" }
navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "navigation3" }
navigationevent-compose = { module = "androidx.navigationevent:navigationevent-compose", version.ref = "navigationevent" }
lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "lifecycle" }