From d8d64c8a7548e61bebb88d74d3f7ca4141740b4a Mon Sep 17 00:00:00 2001 From: LoveSy Date: Tue, 3 Mar 2026 22:39:18 +0800 Subject: [PATCH] Add Compose reboot menu to Home and fix navigation icon padding Replace the View-based RebootMenu with a native Compose SuperListPopup in the Home top bar, with proper state tracking for the safe mode toggle. Remove the now-unused RebootMenu.kt. Also fix missing start padding on the SuperuserDetail back button to match all other sub-screens. Made-with: Cursor --- .../topjohnwu/magisk/ui/home/HomeScreen.kt | 90 ++++++++++++++++++- .../topjohnwu/magisk/ui/home/RebootMenu.kt | 52 ----------- .../ui/superuser/SuperuserDetailScreen.kt | 5 +- 3 files changed, 93 insertions(+), 54 deletions(-) delete mode 100644 app/apk/src/main/java/com/topjohnwu/magisk/ui/home/RebootMenu.kt diff --git a/app/apk/src/main/java/com/topjohnwu/magisk/ui/home/HomeScreen.kt b/app/apk/src/main/java/com/topjohnwu/magisk/ui/home/HomeScreen.kt index 726099e2a..2f6903aac 100644 --- a/app/apk/src/main/java/com/topjohnwu/magisk/ui/home/HomeScreen.kt +++ b/app/apk/src/main/java/com/topjohnwu/magisk/ui/home/HomeScreen.kt @@ -3,8 +3,11 @@ 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 import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow @@ -20,6 +23,10 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -27,18 +34,28 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.unit.dp +import androidx.core.content.getSystemService import androidx.core.net.toUri import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.core.Config +import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Info +import com.topjohnwu.magisk.core.ktx.reboot +import com.topjohnwu.magisk.ui.component.ListPopupDefaults.MenuPositionProvider import com.topjohnwu.magisk.core.R as CoreR import top.yukonga.miuix.kmp.basic.Card +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.TextButton import top.yukonga.miuix.kmp.basic.TopAppBar +import top.yukonga.miuix.kmp.extra.SuperListPopup import top.yukonga.miuix.kmp.theme.MiuixTheme @Composable @@ -51,7 +68,12 @@ fun HomeScreen(viewModel: HomeViewModel) { topBar = { TopAppBar( title = stringResource(CoreR.string.section_home), - scrollBehavior = scrollBehavior + scrollBehavior = scrollBehavior, + actions = { + if (Info.isRooted) { + RebootButton() + } + } ) }, popupHost = { } @@ -88,6 +110,72 @@ fun HomeScreen(viewModel: HomeViewModel) { } } +@Composable +private fun RebootButton() { + val showMenu = remember { mutableStateOf(false) } + val context = LocalContext.current + var safeModeEnabled by remember { mutableIntStateOf(Config.bootloop) } + + val showUserspace = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && + context.getSystemService()?.isRebootingUserspaceSupported == true + val showSafeMode = Const.Version.atLeast_28_0() + + val items = buildList { + add(RebootOption(CoreR.string.reboot) { reboot() }) + if (showUserspace) { + add(RebootOption(CoreR.string.reboot_userspace) { reboot("userspace") }) + } + add(RebootOption(CoreR.string.reboot_recovery) { reboot("recovery") }) + add(RebootOption(CoreR.string.reboot_bootloader) { reboot("bootloader") }) + add(RebootOption(CoreR.string.reboot_download) { reboot("download") }) + add(RebootOption(CoreR.string.reboot_edl) { reboot("edl") }) + if (showSafeMode) { + add(RebootOption(CoreR.string.reboot_safe_mode) { + val newVal = if (safeModeEnabled >= 2) 0 else 2 + Config.bootloop = newVal + safeModeEnabled = newVal + }) + } + } + + Box { + IconButton( + modifier = Modifier.padding(end = 16.dp), + onClick = { showMenu.value = true }, + holdDownState = showMenu.value, + ) { + Icon( + painter = painterResource(R.drawable.ic_restart), + contentDescription = stringResource(CoreR.string.reboot), + ) + } + SuperListPopup( + show = showMenu, + popupPositionProvider = MenuPositionProvider, + alignment = PopupPositionProvider.Align.End, + onDismissRequest = { showMenu.value = false } + ) { + ListPopupColumn { + items.forEachIndexed { index, item -> + val isSafeMode = item.labelRes == CoreR.string.reboot_safe_mode + DropdownImpl( + text = stringResource(item.labelRes), + optionSize = items.size, + isSelected = isSafeMode && safeModeEnabled >= 2, + index = index, + onSelectedIndexChange = { + item.action() + if (!isSafeMode) showMenu.value = false + } + ) + } + } + } + } +} + +private class RebootOption(val labelRes: Int, val action: () -> Unit) + @Composable private fun NoticeCard(onHide: () -> Unit) { Card(modifier = Modifier.fillMaxWidth()) { diff --git a/app/apk/src/main/java/com/topjohnwu/magisk/ui/home/RebootMenu.kt b/app/apk/src/main/java/com/topjohnwu/magisk/ui/home/RebootMenu.kt deleted file mode 100644 index d17eed724..000000000 --- a/app/apk/src/main/java/com/topjohnwu/magisk/ui/home/RebootMenu.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.topjohnwu.magisk.ui.home - -import android.app.Activity -import android.os.Build -import android.os.PowerManager -import android.view.ContextThemeWrapper -import android.view.MenuItem -import android.widget.PopupMenu -import androidx.core.content.getSystemService -import com.topjohnwu.magisk.R -import com.topjohnwu.magisk.core.Config -import com.topjohnwu.magisk.core.Const -import com.topjohnwu.magisk.core.ktx.reboot as systemReboot - -object RebootMenu { - - private fun reboot(item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_reboot_normal -> systemReboot() - R.id.action_reboot_userspace -> systemReboot("userspace") - R.id.action_reboot_bootloader -> systemReboot("bootloader") - R.id.action_reboot_download -> systemReboot("download") - R.id.action_reboot_edl -> systemReboot("edl") - R.id.action_reboot_recovery -> systemReboot("recovery") - R.id.action_reboot_safe_mode -> { - val status = !item.isChecked - item.isChecked = status - Config.bootloop = if (status) 2 else 0 - } - else -> Unit - } - return true - } - - fun inflate(activity: Activity): PopupMenu { - val themeWrapper = ContextThemeWrapper(activity, R.style.Foundation_PopupMenu) - val menu = PopupMenu(themeWrapper, activity.window.decorView) - activity.menuInflater.inflate(R.menu.menu_reboot, menu.menu) - menu.setOnMenuItemClickListener(RebootMenu::reboot) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && - activity.getSystemService()?.isRebootingUserspaceSupported == true) { - menu.menu.findItem(R.id.action_reboot_userspace).isVisible = true - } - if (Const.Version.atLeast_28_0()) { - menu.menu.findItem(R.id.action_reboot_safe_mode).isChecked = Config.bootloop >= 2 - } else { - menu.menu.findItem(R.id.action_reboot_safe_mode).isVisible = false - } - return menu - } - -} diff --git a/app/apk/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserDetailScreen.kt b/app/apk/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserDetailScreen.kt index bfc06cbcc..4c764953f 100644 --- a/app/apk/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserDetailScreen.kt +++ b/app/apk/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserDetailScreen.kt @@ -55,7 +55,10 @@ fun SuperuserDetailScreen( TopAppBar( title = stringResource(CoreR.string.superuser_setting), navigationIcon = { - IconButton(onClick = onBack) { + IconButton( + modifier = Modifier.padding(start = 16.dp), + onClick = onBack + ) { Icon( imageVector = MiuixIcons.Back, contentDescription = stringResource(CoreR.string.back),