diff --git a/manager/app/build.gradle.kts b/manager/app/build.gradle.kts index 7cfb9959..7d3792f4 100644 --- a/manager/app/build.gradle.kts +++ b/manager/app/build.gradle.kts @@ -61,7 +61,6 @@ android { val output = it as BaseVariantOutputImpl output.outputFileName = "KernelSU_${managerVersionName}_${managerVersionCode}-$name.apk" } - kotlin.sourceSets { getByName(name) { kotlin.srcDir("build/generated/ksp/$name/kotlin") @@ -90,10 +89,9 @@ dependencies { implementation(libs.com.google.accompanist.drawablepainter) implementation(libs.com.google.accompanist.navigation.animation) - implementation(libs.com.google.accompanist.systemuicontroller) implementation(libs.com.google.accompanist.webview) - implementation(libs.compose.destinations.animations.core) + implementation(libs.compose.destinations.core) ksp(libs.compose.destinations.ksp) implementation(libs.com.github.topjohnwu.libsu.core) diff --git a/manager/app/src/main/AndroidManifest.xml b/manager/app/src/main/AndroidManifest.xml index 32c920ab..618dd974 100644 --- a/manager/app/src/main/AndroidManifest.xml +++ b/manager/app/src/main/AndroidManifest.xml @@ -22,7 +22,6 @@ android:theme="@style/Theme.KernelSU"> - diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/MainActivity.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/MainActivity.kt index 042155fb..469bf18f 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/MainActivity.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/MainActivity.kt @@ -1,9 +1,15 @@ package me.weishu.kernelsu.ui +import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.material3.Icon import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem @@ -21,12 +27,12 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import com.ramcosta.composedestinations.DestinationsNavHost -import com.ramcosta.composedestinations.navigation.popBackStack +import com.ramcosta.composedestinations.generated.NavGraphs import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState +import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator import me.weishu.kernelsu.Natives import me.weishu.kernelsu.ksuApp import me.weishu.kernelsu.ui.screen.BottomBarDestination -import me.weishu.kernelsu.ui.screen.NavGraphs import me.weishu.kernelsu.ui.theme.KernelSUTheme import me.weishu.kernelsu.ui.util.LocalSnackbarHost import me.weishu.kernelsu.ui.util.rootAvailable @@ -34,6 +40,13 @@ import me.weishu.kernelsu.ui.util.rootAvailable class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { + + // Enable edge to edge + enableEdgeToEdge() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + window.isNavigationBarContrastEnforced = false + } + super.onCreate(savedInstanceState) setContent { @@ -42,7 +55,8 @@ class MainActivity : ComponentActivity() { val snackbarHostState = remember { SnackbarHostState() } Scaffold( bottomBar = { BottomBar(navController) }, - snackbarHost = { SnackbarHost(snackbarHostState) } + snackbarHost = { SnackbarHost(snackbarHostState) }, + contentWindowInsets = WindowInsets(0, 0, 0, 0) ) { innerPadding -> CompositionLocalProvider( LocalSnackbarHost provides snackbarHostState, @@ -61,9 +75,13 @@ class MainActivity : ComponentActivity() { @Composable private fun BottomBar(navController: NavHostController) { + val navigator = navController.rememberDestinationsNavigator() val isManager = Natives.becomeManager(ksuApp.packageName) val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable() - NavigationBar(tonalElevation = 8.dp) { + NavigationBar( + tonalElevation = 8.dp, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Bottom + WindowInsetsSides.Horizontal) + ) { BottomBarDestination.entries.forEach { destination -> if (!fullFeatured && destination.rootRequired) return@forEach val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) @@ -71,11 +89,10 @@ private fun BottomBar(navController: NavHostController) { selected = isCurrentDestOnBackStack, onClick = { if (isCurrentDestOnBackStack) { - navController.popBackStack(destination.direction, false) + navigator.popBackStack(destination.direction, false) } - - navController.navigate(destination.direction.route) { - popUpTo(NavGraphs.root.route) { + navigator.navigate(destination.direction) { + popUpTo(NavGraphs.root) { saveState = true } launchSingleTop = true diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/Dialog.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/Dialog.kt index e2c3fa45..27adc3f0 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/Dialog.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/Dialog.kt @@ -1,6 +1,7 @@ package me.weishu.kernelsu.ui.component import android.graphics.text.LineBreaker +import android.os.Build import android.os.Parcelable import android.text.Layout import android.text.method.LinkMovementMethod @@ -96,8 +97,8 @@ interface ConfirmDialogHandle : DialogHandle { } private abstract class DialogHandleBase( - protected val visible: MutableState, - protected val coroutineScope: CoroutineScope + val visible: MutableState, + val coroutineScope: CoroutineScope ) : DialogHandle { override val isShown: Boolean get() = visible.value @@ -432,7 +433,9 @@ private fun MarkdownContent(content: String) { TextView(context).apply { movementMethod = LinkMovementMethod.getInstance() setSpannableFactory(NoCopySpannableFactory.getInstance()) - breakStrategy = LineBreaker.BREAK_STRATEGY_SIMPLE + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + breakStrategy = LineBreaker.BREAK_STRATEGY_SIMPLE + } hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt index 8195cddd..b6f7dbe7 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt @@ -5,8 +5,12 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons @@ -132,7 +136,8 @@ fun SearchAppBar( dropdownContent() } - } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt index d2cb3f34..1a39dcc3 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt @@ -1,11 +1,10 @@ -@file:OptIn(ExperimentalMaterial3Api::class) - package me.weishu.kernelsu.ui.component.profile import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions @@ -20,12 +19,14 @@ import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedCard import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable 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 @@ -86,7 +87,7 @@ fun RootProfileConfig( ) { OutlinedTextField( modifier = Modifier - .menuAnchor() + .menuAnchor(MenuAnchorType.PrimaryNotEditable) .fillMaxWidth(), readOnly = true, label = { Text(stringResource(R.string.profile_namespace)) }, @@ -184,7 +185,7 @@ fun RootProfileConfig( } } -@OptIn(ExperimentalLayoutApi::class) +@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) @Composable fun GroupsPanel(selected: List, closeSelection: (selection: Set) -> Unit) { val selectGroupsDialog = rememberCustomDialog { dismiss: () -> Unit -> @@ -234,14 +235,20 @@ fun GroupsPanel(selected: List, closeSelection: (selection: Set) ) } - OutlinedCard(modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - .clickable { - selectGroupsDialog.show() - }) { + OutlinedCard( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { - Column(modifier = Modifier.padding(16.dp)) { + Column( + modifier = Modifier + .fillMaxSize() + .clickable { + selectGroupsDialog.show() + } + .padding(16.dp) + ) { Text(stringResource(R.string.profile_groups)) FlowRow { selected.forEach { group -> @@ -256,7 +263,7 @@ fun GroupsPanel(selected: List, closeSelection: (selection: Set) } } -@OptIn(ExperimentalLayoutApi::class) +@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) @Composable fun CapsPanel( selected: Collection, @@ -299,14 +306,20 @@ fun CapsPanel( ) } - OutlinedCard(modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - .clickable { - selectCapabilitiesDialog.show() - }) { + OutlinedCard( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { - Column(modifier = Modifier.padding(16.dp)) { + Column( + modifier = Modifier + .fillMaxSize() + .clickable { + selectCapabilitiesDialog.show() + } + .padding(16.dp) + ) { Text(stringResource(R.string.profile_capabilities)) FlowRow { selected.forEach { group -> @@ -329,10 +342,10 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) { mutableStateOf(false) } var lastValidUid by remember { - mutableStateOf(uid) + mutableIntStateOf(uid) } - val keyboardController = LocalSoftwareKeyboardController.current + OutlinedTextField( modifier = Modifier.fillMaxWidth(), label = { Text(label) }, @@ -365,6 +378,7 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) { }) } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun SELinuxPanel( profile: Natives.Profile, @@ -452,7 +466,7 @@ private fun SELinuxPanel( ), label = { Text(text = stringResource(R.string.profile_selinux_context)) }, value = profile.context, - onValueChange = { }, + onValueChange = { } ) }) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt index d09a2343..b60e8ea4 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/TemplateConfig.kt @@ -12,6 +12,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem +import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -54,7 +55,7 @@ fun TemplateConfig( ) { OutlinedTextField( modifier = Modifier - .menuAnchor() + .menuAnchor(MenuAnchorType.PrimaryNotEditable) .fillMaxWidth(), readOnly = true, label = { Text(stringResource(R.string.profile_template)) }, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt index ca53388f..f7035a67 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt @@ -7,10 +7,14 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -50,6 +54,9 @@ import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import coil.request.ImageRequest import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination +import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.launch import me.weishu.kernelsu.Natives @@ -58,8 +65,6 @@ import me.weishu.kernelsu.ui.component.SwitchItem import me.weishu.kernelsu.ui.component.profile.AppProfileConfig import me.weishu.kernelsu.ui.component.profile.RootProfileConfig import me.weishu.kernelsu.ui.component.profile.TemplateConfig -import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination -import me.weishu.kernelsu.ui.screen.destinations.TemplateEditorScreenDestination import me.weishu.kernelsu.ui.util.LocalSnackbarHost import me.weishu.kernelsu.ui.util.forceStopApp import me.weishu.kernelsu.ui.util.getSepolicy @@ -73,7 +78,7 @@ import me.weishu.kernelsu.ui.viewmodel.getTemplateInfoById * @author weishu * @date 2023/5/16. */ -@Destination +@Destination @Composable fun AppProfileScreen( navigator: DestinationsNavigator, @@ -82,10 +87,8 @@ fun AppProfileScreen( val context = LocalContext.current val snackbarHost = LocalSnackbarHost.current val scope = rememberCoroutineScope() - val failToUpdateAppProfile = - stringResource(R.string.failed_to_update_app_profile).format(appInfo.label) - val failToUpdateSepolicy = - stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label) + val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label) + val failToUpdateSepolicy = stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label) val packageName = appInfo.packageName val initialProfile = Natives.getAppProfile(packageName, appInfo.uid) @@ -98,6 +101,7 @@ fun AppProfileScreen( Scaffold( topBar = { TopBar { navigator.popBackStack() } }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { paddingValues -> AppProfileInner( modifier = Modifier @@ -248,6 +252,7 @@ private fun TopBar(onBack: () -> Unit) { onClick = onBack ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/BottomBarDestination.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/BottomBarDestination.kt index 9345ced5..c9637ed2 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/BottomBarDestination.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/BottomBarDestination.kt @@ -5,11 +5,11 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.* import androidx.compose.ui.graphics.vector.ImageVector +import com.ramcosta.composedestinations.generated.destinations.HomeScreenDestination +import com.ramcosta.composedestinations.generated.destinations.ModuleScreenDestination +import com.ramcosta.composedestinations.generated.destinations.SuperUserScreenDestination import com.ramcosta.composedestinations.spec.DirectionDestinationSpec import me.weishu.kernelsu.R -import me.weishu.kernelsu.ui.screen.destinations.HomeScreenDestination -import me.weishu.kernelsu.ui.screen.destinations.SuperUserScreenDestination -import me.weishu.kernelsu.ui.screen.destinations.ModuleScreenDestination enum class BottomBarDestination( val direction: DirectionDestinationSpec, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt index 1e3d48fd..3afdfb85 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt @@ -4,8 +4,12 @@ import android.net.Uri import android.os.Environment import android.os.Parcelable import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -35,6 +39,7 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import kotlinx.coroutines.Dispatchers @@ -66,7 +71,7 @@ enum class FlashingStatus { * @date 2023/1/1. */ @Composable -@Destination +@Destination fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { var text by rememberSaveable { mutableStateOf("") } @@ -139,8 +144,8 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { text = { Text(text = reboot) }, ) } - - } + }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { innerPadding -> KeyEventBlocker { it.key == Key.VolumeDown || it.key == Key.VolumeUp @@ -227,7 +232,8 @@ private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () - contentDescription = "Localized description" ) } - } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt index b1e99888..7fcbb2df 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt @@ -27,32 +27,35 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.core.content.pm.PackageInfoCompat import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination +import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import me.weishu.kernelsu.* import me.weishu.kernelsu.R import me.weishu.kernelsu.ui.component.rememberConfirmDialog -import me.weishu.kernelsu.ui.screen.destinations.InstallScreenDestination -import me.weishu.kernelsu.ui.screen.destinations.SettingScreenDestination import me.weishu.kernelsu.ui.util.* import me.weishu.kernelsu.ui.util.module.LatestVersionInfo -@RootNavGraph(start = true) -@Destination +@Destination(start = true) @Composable fun HomeScreen(navigator: DestinationsNavigator) { val kernelVersion = getKernelVersion() - Scaffold(topBar = { - TopBar(kernelVersion, onSettingsClick = { - navigator.navigate(SettingScreenDestination) - }, onInstallClick = { - navigator.navigate(InstallScreenDestination) - }) - }) { innerPadding -> + Scaffold( + topBar = { + TopBar(kernelVersion, onSettingsClick = { + navigator.navigate(SettingScreenDestination) + }, onInstallClick = { + navigator.navigate(InstallScreenDestination) + }) + }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + ) { innerPadding -> Column( modifier = Modifier .padding(innerPadding) @@ -103,12 +106,11 @@ fun UpdateCard() { val context = LocalContext.current val latestVersionInfo = LatestVersionInfo() val newVersion by produceState(initialValue = latestVersionInfo) { - value = withContext(Dispatchers.IO){ + value = withContext(Dispatchers.IO) { checkNewVersion() } } - val currentVersionCode = getManagerVersion(context).second val newVersionCode = newVersion.versionCode val newVersionUrl = newVersion.downloadUrl @@ -158,50 +160,54 @@ private fun TopBar( onInstallClick: () -> Unit, onSettingsClick: () -> Unit ) { - TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = { - if (kernelVersion.isGKI()) { - IconButton(onClick = onInstallClick) { + TopAppBar( + title = { Text(stringResource(R.string.app_name)) }, + actions = { + if (kernelVersion.isGKI()) { + IconButton(onClick = onInstallClick) { + Icon( + imageVector = Icons.Filled.Archive, + contentDescription = stringResource(id = R.string.install) + ) + } + } + + var showDropdown by remember { mutableStateOf(false) } + IconButton(onClick = { + showDropdown = true + }) { Icon( - imageVector = Icons.Filled.Archive, - contentDescription = stringResource(id = R.string.install) + imageVector = Icons.Filled.Refresh, + contentDescription = stringResource(id = R.string.reboot) + ) + + DropdownMenu(expanded = showDropdown, onDismissRequest = { + showDropdown = false + }) { + + RebootDropdownItem(id = R.string.reboot) + + val pm = + LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager? + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) { + RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace") + } + RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery") + RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader") + RebootDropdownItem(id = R.string.reboot_download, reason = "download") + RebootDropdownItem(id = R.string.reboot_edl, reason = "edl") + } + } + + IconButton(onClick = onSettingsClick) { + Icon( + imageVector = Icons.Filled.Settings, + contentDescription = stringResource(id = R.string.settings) ) } - } - - var showDropdown by remember { mutableStateOf(false) } - IconButton(onClick = { - showDropdown = true - }) { - Icon( - imageVector = Icons.Filled.Refresh, - contentDescription = stringResource(id = R.string.reboot) - ) - - DropdownMenu(expanded = showDropdown, onDismissRequest = { - showDropdown = false - }) { - - RebootDropdownItem(id = R.string.reboot) - - val pm = - LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager? - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) { - RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace") - } - RebootDropdownItem(id = R.string.reboot_recovery, reason = "recovery") - RebootDropdownItem(id = R.string.reboot_bootloader, reason = "bootloader") - RebootDropdownItem(id = R.string.reboot_download, reason = "download") - RebootDropdownItem(id = R.string.reboot_edl, reason = "edl") - } - } - - IconButton(onClick = onSettingsClick) { - Icon( - imageVector = Icons.Filled.Settings, - contentDescription = stringResource(id = R.string.settings) - ) - } - }) + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + ) } @Composable @@ -415,9 +421,10 @@ private fun InfoCard() { } } -fun getManagerVersion(context: Context): Pair { +fun getManagerVersion(context: Context): Pair { val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)!! - return Pair(packageInfo.versionName!!, packageInfo.versionCode) + val versionCode = PackageInfoCompat.getLongVersionCode(packageInfo) + return Pair(packageInfo.versionName!!, versionCode) } @Preview diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt index 71b5c975..b0f5d7ac 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt @@ -9,8 +9,12 @@ import androidx.annotation.StringRes import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.FileUpload @@ -40,13 +44,14 @@ import com.maxkeppeler.sheets.list.ListDialog import com.maxkeppeler.sheets.list.models.ListOption import com.maxkeppeler.sheets.list.models.ListSelection import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import me.weishu.kernelsu.R import me.weishu.kernelsu.ui.component.DialogHandle import me.weishu.kernelsu.ui.component.rememberConfirmDialog import me.weishu.kernelsu.ui.component.rememberCustomDialog -import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination import me.weishu.kernelsu.ui.util.LkmSelection import me.weishu.kernelsu.ui.util.getCurrentKmi import me.weishu.kernelsu.ui.util.getSupportedKmis @@ -58,7 +63,7 @@ import me.weishu.kernelsu.ui.util.rootAvailable * @author weishu * @date 2024/3/12. */ -@Destination +@Destination @Composable fun InstallScreen(navigator: DestinationsNavigator) { var installMethod by remember { @@ -113,11 +118,14 @@ fun InstallScreen(navigator: DestinationsNavigator) { }) } - Scaffold(topBar = { - TopBar( - onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload - ) - }) { + Scaffold( + topBar = { + TopBar( + onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload + ) + }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + ) { Column(modifier = Modifier.padding(it)) { SelectInstallMethod { method -> installMethod = method @@ -293,15 +301,18 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle { @OptIn(ExperimentalMaterial3Api::class) @Composable private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) { - TopAppBar(title = { Text(stringResource(R.string.install)) }, navigationIcon = { - IconButton( - onClick = onBack - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, actions = { - IconButton(onClick = onLkmUpload) { - Icon(Icons.Filled.FileUpload, contentDescription = null) - } - }) + TopAppBar( + title = { Text(stringResource(R.string.install)) }, navigationIcon = { + IconButton( + onClick = onBack + ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } + }, actions = { + IconButton(onClick = onLkmUpload) { + Icon(Icons.Filled.FileUpload, contentDescription = null) + } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + ) } @Composable diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt index 9812e87e..c224dfa3 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt @@ -14,11 +14,15 @@ 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.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape @@ -29,7 +33,6 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.Button -import androidx.compose.material3.CardDefaults import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExtendedFloatingActionButton @@ -64,6 +67,8 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -73,7 +78,6 @@ import me.weishu.kernelsu.R import me.weishu.kernelsu.ui.component.ConfirmResult import me.weishu.kernelsu.ui.component.rememberConfirmDialog import me.weishu.kernelsu.ui.component.rememberLoadingDialog -import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination import me.weishu.kernelsu.ui.util.DownloadListener import me.weishu.kernelsu.ui.util.LocalSnackbarHost import me.weishu.kernelsu.ui.util.download @@ -85,7 +89,7 @@ import me.weishu.kernelsu.ui.viewmodel.ModuleViewModel import me.weishu.kernelsu.ui.webui.WebUIActivity import okhttp3.OkHttpClient -@Destination +@Destination @Composable fun ModuleScreen(navigator: DestinationsNavigator) { val viewModel = viewModel() @@ -102,41 +106,46 @@ fun ModuleScreen(navigator: DestinationsNavigator) { val hideInstallButton = isSafeMode || hasMagisk - Scaffold(topBar = { - TopBar() - }, floatingActionButton = if (hideInstallButton) { - { /* Empty */ } - } else { - { - val moduleInstall = stringResource(id = R.string.module_install) - val selectZipLauncher = rememberLauncherForActivityResult( - contract = ActivityResultContracts.StartActivityForResult() - ) { - if (it.resultCode != RESULT_OK) { - return@rememberLauncherForActivityResult + Scaffold( + topBar = { + TopBar() + }, + floatingActionButton = { + if (hideInstallButton) { + /* Empty */ + } else { + val moduleInstall = stringResource(id = R.string.module_install) + val selectZipLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult() + ) { + if (it.resultCode != RESULT_OK) { + return@rememberLauncherForActivityResult + } + val data = it.data ?: return@rememberLauncherForActivityResult + val uri = data.data ?: return@rememberLauncherForActivityResult + + navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri))) + + viewModel.markNeedRefresh() + + Log.i("ModuleScreen", "select zip result: ${it.data}") } - val data = it.data ?: return@rememberLauncherForActivityResult - val uri = data.data ?: return@rememberLauncherForActivityResult - navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri))) + ExtendedFloatingActionButton( + onClick = { + // select the zip file to install + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "application/zip" + selectZipLauncher.launch(intent) + }, + icon = { Icon(Icons.Filled.Add, moduleInstall) }, + text = { Text(text = moduleInstall) }, + ) - viewModel.markNeedRefresh() - - Log.i("ModuleScreen", "select zip result: ${it.data}") } - - ExtendedFloatingActionButton( - onClick = { - // select the zip file to install - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.type = "application/zip" - selectZipLauncher.launch(intent) - }, - icon = { Icon(Icons.Filled.Add, moduleInstall) }, - text = { Text(text = moduleInstall) }, - ) - } - }) { innerPadding -> + }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + ) { innerPadding -> when { hasMagisk -> { @@ -163,10 +172,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) { navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it))) }, onClickModule = { id, name, hasWebUi -> if (hasWebUi) { - context.startActivity(Intent(context, WebUIActivity::class.java) - .setData(Uri.parse("kernelsu://webui/$id")) - .putExtra("id", id) - .putExtra("name", name) + context.startActivity( + Intent(context, WebUIActivity::class.java) + .setData(Uri.parse("kernelsu://webui/$id")) + .putExtra("id", id) + .putExtra("name", name) ) } }) @@ -419,7 +429,10 @@ private fun ModuleList( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun TopBar() { - TopAppBar(title = { Text(stringResource(R.string.module)) }) + TopAppBar( + title = { Text(stringResource(R.string.module)) }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + ) } @Composable @@ -433,13 +446,16 @@ private fun ModuleItem( onClick: (ModuleViewModel.ModuleInfo) -> Unit ) { ElevatedCard( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface) + modifier = Modifier.fillMaxWidth() ) { val textDecoration = if (!module.remove) null else TextDecoration.LineThrough - Column(modifier = Modifier.clickable { onClick(module) }.padding(24.dp, 16.dp, 24.dp, 0.dp)) { + Column( + modifier = Modifier + .clickable { onClick(module) } + .padding(24.dp, 16.dp, 24.dp, 0.dp) + ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt index 4f970f7e..a0f32d80 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt @@ -1,18 +1,18 @@ package me.weishu.kernelsu.ui.screen -import android.content.ContentResolver import android.content.Context import android.content.Intent -import android.database.Cursor import android.net.Uri -import android.provider.OpenableColumns -import android.util.Log import android.widget.Toast import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -29,7 +29,6 @@ import androidx.compose.material.icons.filled.RemoveModerator import androidx.compose.material.icons.filled.Save import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Update -import androidx.compose.material3.BottomSheetScaffold import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -62,6 +61,9 @@ import com.maxkeppeler.sheets.list.ListDialog import com.maxkeppeler.sheets.list.models.ListOption import com.maxkeppeler.sheets.list.models.ListSelection import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination +import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import kotlinx.coroutines.Dispatchers @@ -77,20 +79,16 @@ import me.weishu.kernelsu.ui.component.SwitchItem import me.weishu.kernelsu.ui.component.rememberConfirmDialog import me.weishu.kernelsu.ui.component.rememberCustomDialog import me.weishu.kernelsu.ui.component.rememberLoadingDialog -import me.weishu.kernelsu.ui.screen.destinations.AppProfileTemplateScreenDestination -import me.weishu.kernelsu.ui.screen.destinations.FlashScreenDestination import me.weishu.kernelsu.ui.util.getBugreportFile import me.weishu.kernelsu.ui.util.getFileNameFromUri import me.weishu.kernelsu.ui.util.shrinkModules -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter /** * @author weishu * @date 2023/1/1. */ @OptIn(ExperimentalMaterial3Api::class) -@Destination +@Destination @Composable fun SettingScreen(navigator: DestinationsNavigator) { Scaffold( @@ -98,7 +96,8 @@ fun SettingScreen(navigator: DestinationsNavigator) { TopBar(onBack = { navigator.popBackStack() }) - } + }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { paddingValues -> val aboutDialog = rememberCustomDialog { AboutDialog(it) @@ -184,17 +183,20 @@ fun SettingScreen(navigator: DestinationsNavigator) { showBottomsheet = true } ) - if (showBottomsheet){ + if (showBottomsheet) { ModalBottomSheet( onDismissRequest = { showBottomsheet = false }, content = { - Row(modifier = Modifier.padding(10.dp) - .align(Alignment.CenterHorizontally) + Row( + modifier = Modifier + .padding(10.dp) + .align(Alignment.CenterHorizontally) ) { - Box{ + Box { Column( - modifier = Modifier.padding(16.dp) + modifier = Modifier + .padding(16.dp) .clickable { scope.launch { val bugreport = loadingDialog.withLoading { @@ -209,14 +211,15 @@ fun SettingScreen(navigator: DestinationsNavigator) { "${BuildConfig.APPLICATION_ID}.fileprovider", bugreport ) - val filename = getFileNameFromUri(context , uri) - val savefile = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { - addCategory(Intent.CATEGORY_OPENABLE) - type = "application/zip" - putExtra(Intent.EXTRA_STREAM, uri) - putExtra(Intent.EXTRA_TITLE, filename) - flags = Intent.FLAG_GRANT_READ_URI_PERMISSION - } + val filename = getFileNameFromUri(context, uri) + val savefile = + Intent(Intent.ACTION_CREATE_DOCUMENT).apply { + addCategory(Intent.CATEGORY_OPENABLE) + type = "application/zip" + putExtra(Intent.EXTRA_STREAM, uri) + putExtra(Intent.EXTRA_TITLE, filename) + flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + } context.startActivity( Intent.createChooser( savefile, @@ -245,9 +248,10 @@ fun SettingScreen(navigator: DestinationsNavigator) { } } - Box{ + Box { Column( - modifier = Modifier.padding(16.dp) + modifier = Modifier + .padding(16.dp) .clickable { scope.launch { val bugreport = loadingDialog.withLoading { @@ -350,6 +354,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { } } } + @Composable fun UninstallItem( navigator: DestinationsNavigator, @@ -374,11 +379,9 @@ fun UninstallItem( UninstallType.PERMANENT -> navigator.navigate( FlashScreenDestination(FlashIt.FlashUninstall) ) - UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate( FlashScreenDestination(FlashIt.FlashRestore) ) - UninstallType.NONE -> Unit } } @@ -464,6 +467,7 @@ private fun TopBar(onBack: () -> Unit = {}) { onClick = onBack ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt index 2eb2e770..e41345b0 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt @@ -26,16 +26,17 @@ import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import coil.request.ImageRequest import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.generated.destinations.AppProfileScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.launch import me.weishu.kernelsu.Natives import me.weishu.kernelsu.R import me.weishu.kernelsu.ui.component.SearchAppBar -import me.weishu.kernelsu.ui.screen.destinations.AppProfileScreenDestination import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel @OptIn(ExperimentalMaterialApi::class) -@Destination +@Destination @Composable fun SuperUserScreen(navigator: DestinationsNavigator) { val viewModel = viewModel() @@ -92,7 +93,8 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { } }, ) - } + }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { innerPadding -> val refreshState = rememberPullRefreshState( refreshing = viewModel.isRefreshing, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt index bf353b0a..1ba58059 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt @@ -7,8 +7,12 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.ExperimentalMaterialApi @@ -47,13 +51,14 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph +import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.result.ResultRecipient import com.ramcosta.composedestinations.result.getOr import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import me.weishu.kernelsu.R -import me.weishu.kernelsu.ui.screen.destinations.TemplateEditorScreenDestination import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel /** @@ -62,7 +67,7 @@ import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel */ @OptIn(ExperimentalMaterialApi::class) -@Destination +@Destination @Composable fun AppProfileTemplateScreen( navigator: DestinationsNavigator, @@ -141,6 +146,7 @@ fun AppProfileTemplateScreen( text = { Text(stringResource(id = R.string.app_profile_template_create)) }, ) }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { innerPadding -> val refreshState = rememberPullRefreshState( refreshing = viewModel.isRefreshing, @@ -254,6 +260,7 @@ private fun TopBar( }) } } - } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt index b6b7cc80..53f5beff 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt @@ -3,8 +3,12 @@ package me.weishu.kernelsu.ui.screen import android.widget.Toast import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions @@ -37,6 +41,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.result.ResultBackNavigator import me.weishu.kernelsu.Natives import me.weishu.kernelsu.R @@ -52,7 +57,7 @@ import me.weishu.kernelsu.ui.viewmodel.toJSON * @date 2023/10/20. */ @OptIn(ExperimentalComposeUiApi::class) -@Destination +@Destination @Composable fun TemplateEditorScreen( navigator: ResultBackNavigator, @@ -108,6 +113,7 @@ fun TemplateEditorScreen( } }) }, + contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { innerPadding -> Column( modifier = Modifier @@ -242,37 +248,40 @@ private fun TopBar( onDelete: () -> Unit = {}, onSave: () -> Unit = {} ) { - TopAppBar(title = { - Column { - Text(title) - if (summary.isNotBlank()) { - Text( - text = summary, - style = MaterialTheme.typography.bodyMedium, + TopAppBar( + title = { + Column { + Text(title) + if (summary.isNotBlank()) { + Text( + text = summary, + style = MaterialTheme.typography.bodyMedium, + ) + } + } + }, navigationIcon = { + IconButton( + onClick = onBack + ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } + }, actions = { + if (readOnly) { + return@TopAppBar + } + IconButton(onClick = onDelete) { + Icon( + Icons.Filled.DeleteForever, + contentDescription = stringResource(id = R.string.app_profile_template_delete) ) } - } - }, navigationIcon = { - IconButton( - onClick = onBack - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } - }, actions = { - if (readOnly) { - return@TopAppBar - } - IconButton(onClick = onDelete) { - Icon( - Icons.Filled.DeleteForever, - contentDescription = stringResource(id = R.string.app_profile_template_delete) - ) - } - IconButton(onClick = onSave) { - Icon( - imageVector = Icons.Filled.Save, - contentDescription = stringResource(id = R.string.app_profile_template_save) - ) - } - }) + IconButton(onClick = onSave) { + Icon( + imageVector = Icons.Filled.Save, + contentDescription = stringResource(id = R.string.app_profile_template_save) + ) + } + }, + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + ) } @Composable @@ -289,17 +298,16 @@ private fun TextEdit( value = text, modifier = Modifier.fillMaxWidth(), label = { Text(label) }, - suffix = - if (errorHint.isNotBlank()) { - { + suffix = { + if (errorHint.isNotBlank()) { Text( text = if (isError) errorHint else "", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.error ) + } else { + null } - } else { - null }, isError = isError, keyboardOptions = KeyboardOptions( diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt index 3b3945d0..903ee94e 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/theme/Theme.kt @@ -7,12 +7,8 @@ import androidx.compose.material3.darkColorScheme import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.lightColorScheme -import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.dp -import com.google.accompanist.systemuicontroller.rememberSystemUiController private val DarkColorScheme = darkColorScheme( primary = YELLOW, @@ -42,20 +38,6 @@ fun KernelSUTheme( else -> LightColorScheme } - val systemUiController = rememberSystemUiController() - SideEffect { - systemUiController.setStatusBarColor( - color = colorScheme.surface, - darkIcons = !darkTheme - ) - - // To match the App Navbar color - systemUiController.setNavigationBarColor( - color = colorScheme.surfaceColorAtElevation(8.dp), - darkIcons = !darkTheme, - ) - } - MaterialTheme( colorScheme = colorScheme, typography = Typography, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt index 19eb1df1..a8363120 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/LogEvent.kt @@ -1,16 +1,12 @@ package me.weishu.kernelsu.ui.util -import android.content.ContentResolver import android.content.Context -import android.net.Uri import android.os.Build -import android.os.ParcelFileDescriptor import android.system.Os import com.topjohnwu.superuser.ShellUtils import me.weishu.kernelsu.Natives import me.weishu.kernelsu.ui.screen.getManagerVersion import java.io.File -import java.io.FileOutputStream import java.io.FileWriter import java.io.PrintWriter import java.time.LocalDateTime diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt index 78346dd9..6b0d704e 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/SELinuxChecker.kt @@ -1,7 +1,7 @@ package me.weishu.kernelsu.ui.util -import androidx.compose.ui.res.stringResource import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource import com.topjohnwu.superuser.Shell import me.weishu.kernelsu.R diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt index 18774910..d926d7ea 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebUIActivity.kt @@ -2,13 +2,18 @@ package me.weishu.kernelsu.ui.webui import android.annotation.SuppressLint import android.app.ActivityManager -import android.content.Context +import android.os.Build import android.os.Bundle +import android.view.ViewGroup.MarginLayoutParams import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView import android.webkit.WebViewClient import androidx.activity.ComponentActivity +import androidx.activity.enableEdgeToEdge +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams import androidx.webkit.WebViewAssetLoader import com.topjohnwu.superuser.Shell import me.weishu.kernelsu.ui.util.createRootShell @@ -21,12 +26,26 @@ class WebUIActivity : ComponentActivity() { private var rootShell: Shell? = null override fun onCreate(savedInstanceState: Bundle?) { + + // Enable edge to edge + enableEdgeToEdge() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + window.isNavigationBarContrastEnforced = false + } + super.onCreate(savedInstanceState) + val moduleId = intent.getStringExtra("id")!! val name = intent.getStringExtra("name")!! - setTaskDescription(ActivityManager.TaskDescription("KernelSU - $name")) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + @Suppress("DEPRECATION") + setTaskDescription(ActivityManager.TaskDescription("KernelSU - $name")) + } else { + val taskDescription = ActivityManager.TaskDescription.Builder().setLabel("KernelSU - $name").build() + setTaskDescription(taskDescription) + } - val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE) + val prefs = getSharedPreferences("settings", MODE_PRIVATE) WebView.setWebContentsDebuggingEnabled(prefs.getBoolean("enable_web_debugging", false)) val moduleDir = "/data/adb/modules/${moduleId}" @@ -50,6 +69,16 @@ class WebUIActivity : ComponentActivity() { } val webView = WebView(this).apply { + ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets -> + val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + view.updateLayoutParams { + leftMargin = inset.left + rightMargin = inset.right + topMargin = inset.top + bottomMargin = inset.bottom + } + return@setOnApplyWindowInsetsListener insets + } settings.javaScriptEnabled = true settings.domStorageEnabled = true settings.allowFileAccess = false diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt index 394c237f..00fcde65 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewInterface.kt @@ -9,21 +9,24 @@ import android.view.Window import android.webkit.JavascriptInterface import android.webkit.WebView import android.widget.Toast -import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat import com.topjohnwu.superuser.CallbackList import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.internal.UiThreadHandler -import me.weishu.kernelsu.ui.util.listModules import me.weishu.kernelsu.ui.util.createRootShell +import me.weishu.kernelsu.ui.util.listModules import me.weishu.kernelsu.ui.util.withNewRootShell import org.json.JSONArray import org.json.JSONObject -import java.util.concurrent.CompletableFuture import java.io.File +import java.util.concurrent.CompletableFuture -class WebViewInterface(val context: Context, private val webView: WebView, private val modDir: String) { +class WebViewInterface( + val context: Context, + private val webView: WebView, + private val modDir: String +) { @JavascriptInterface fun exec(cmd: String): String { @@ -187,28 +190,20 @@ class WebViewInterface(val context: Context, private val webView: WebView, priva } var keys = currentInfo.keys() - for(key in keys) { - currentModuleInfo.put(key, currentInfo.get(key)); + for (key in keys) { + currentModuleInfo.put(key, currentInfo.get(key)) } - break; + break } - return currentModuleInfo.toString(); + return currentModuleInfo.toString() } } -fun hideSystemUI(window: Window) { - WindowCompat.setDecorFitsSystemWindows(window, false) +fun hideSystemUI(window: Window) = WindowInsetsControllerCompat(window, window.decorView).let { controller -> controller.hide(WindowInsetsCompat.Type.systemBars()) - controller.systemBarsBehavior = - WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE } -} -fun showSystemUI(window: Window) { - WindowCompat.setDecorFitsSystemWindows(window, true) - WindowInsetsControllerCompat( - window, - window.decorView - ).show(WindowInsetsCompat.Type.systemBars()) -} \ No newline at end of file +fun showSystemUI(window: Window) = + WindowInsetsControllerCompat(window, window.decorView).show(WindowInsetsCompat.Type.systemBars()) diff --git a/manager/app/src/main/res/values-night-v27/themes.xml b/manager/app/src/main/res/values-night-v27/themes.xml deleted file mode 100644 index 10a773b1..00000000 --- a/manager/app/src/main/res/values-night-v27/themes.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/values-night/themes.xml b/manager/app/src/main/res/values-night/themes.xml index 91abf657..d76ba8e6 100644 --- a/manager/app/src/main/res/values-night/themes.xml +++ b/manager/app/src/main/res/values-night/themes.xml @@ -1,10 +1,10 @@ - + - + + - \ No newline at end of file diff --git a/manager/app/src/main/res/values/themes.xml b/manager/app/src/main/res/values/themes.xml index 7d41d8ec..31721d39 100644 --- a/manager/app/src/main/res/values/themes.xml +++ b/manager/app/src/main/res/values/themes.xml @@ -1,10 +1,13 @@ - + - + \ No newline at end of file diff --git a/manager/gradle/libs.versions.toml b/manager/gradle/libs.versions.toml index 4201fdd8..30c2bf85 100644 --- a/manager/gradle/libs.versions.toml +++ b/manager/gradle/libs.versions.toml @@ -1,18 +1,18 @@ [versions] -agp = "8.5.2" +agp = "8.6.1" kotlin = "2.0.20" -ksp = "2.0.20-1.0.24" -compose-bom = "2024.08.00" -lifecycle = "2.8.4" -accompanist = "0.34.0" -navigation = "2.7.7" -activity-compose = "1.9.1" -kotlinx-coroutines = "1.8.1" +ksp = "2.0.20-1.0.25" +compose-bom = "2024.09.02" +lifecycle = "2.8.6" +accompanist = "0.36.0" +navigation = "2.8.1" +activity-compose = "1.9.2" +kotlinx-coroutines = "1.9.0" coil-compose = "2.7.0" -compose-destination = "1.10.2" +compose-destination = "2.1.0-beta12" sheets-compose-dialogs = "1.3.0" markdown = "4.6.2" -webkit = "1.11.0" +webkit = "1.12.0" appiconloader-coil = "1.5.0" parcelablelist = "2.0.1" libsu = "6.0.0" @@ -53,7 +53,6 @@ androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" } com-google-accompanist-drawablepainter = { group = "com.google.accompanist", name = "accompanist-drawablepainter", version.ref = "accompanist" } com-google-accompanist-navigation-animation = { group = "com.google.accompanist", name = "accompanist-navigation-animation", version.ref = "accompanist" } -com-google-accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" } com-google-accompanist-webview = { group = "com.google.accompanist", name = "accompanist-webview", version.ref = "accompanist" } com-github-topjohnwu-libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" } @@ -68,7 +67,7 @@ kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-c me-zhanghai-android-appiconloader-coil = { group = "me.zhanghai.android.appiconloader", name = "appiconloader-coil", version.ref = "appiconloader-coil" } -compose-destinations-animations-core = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "compose-destination" } +compose-destinations-core = { group = "io.github.raamcosta.compose-destinations", name = "core", version.ref = "compose-destination" } compose-destinations-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "compose-destination" } sheet-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "core", version.ref = "sheets-compose-dialogs" }