manager: Insets stuffs for Android 15 (#2100)

Fix insets on Android 15
Bump dependencies
Migrate Compose Destination to v2

Ready for review now
This commit is contained in:
Light_summer
2024-09-29 15:45:52 +08:00
committed by GitHub
parent f65aaff4e3
commit fbf3d3945b
27 changed files with 411 additions and 318 deletions

View File

@@ -61,7 +61,6 @@ android {
val output = it as BaseVariantOutputImpl val output = it as BaseVariantOutputImpl
output.outputFileName = "KernelSU_${managerVersionName}_${managerVersionCode}-$name.apk" output.outputFileName = "KernelSU_${managerVersionName}_${managerVersionCode}-$name.apk"
} }
kotlin.sourceSets { kotlin.sourceSets {
getByName(name) { getByName(name) {
kotlin.srcDir("build/generated/ksp/$name/kotlin") kotlin.srcDir("build/generated/ksp/$name/kotlin")
@@ -90,10 +89,9 @@ dependencies {
implementation(libs.com.google.accompanist.drawablepainter) implementation(libs.com.google.accompanist.drawablepainter)
implementation(libs.com.google.accompanist.navigation.animation) implementation(libs.com.google.accompanist.navigation.animation)
implementation(libs.com.google.accompanist.systemuicontroller)
implementation(libs.com.google.accompanist.webview) implementation(libs.com.google.accompanist.webview)
implementation(libs.compose.destinations.animations.core) implementation(libs.compose.destinations.core)
ksp(libs.compose.destinations.ksp) ksp(libs.compose.destinations.ksp)
implementation(libs.com.github.topjohnwu.libsu.core) implementation(libs.com.github.topjohnwu.libsu.core)

View File

@@ -22,7 +22,6 @@
android:theme="@style/Theme.KernelSU"> android:theme="@style/Theme.KernelSU">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>

View File

@@ -1,9 +1,15 @@
package me.weishu.kernelsu.ui package me.weishu.kernelsu.ui
import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent 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.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItem
@@ -21,12 +27,12 @@ import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.ramcosta.composedestinations.DestinationsNavHost 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.isRouteOnBackStackAsState
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
import me.weishu.kernelsu.Natives import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ksuApp import me.weishu.kernelsu.ksuApp
import me.weishu.kernelsu.ui.screen.BottomBarDestination 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.theme.KernelSUTheme
import me.weishu.kernelsu.ui.util.LocalSnackbarHost import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.rootAvailable import me.weishu.kernelsu.ui.util.rootAvailable
@@ -34,6 +40,13 @@ import me.weishu.kernelsu.ui.util.rootAvailable
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { 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) super.onCreate(savedInstanceState)
setContent { setContent {
@@ -42,7 +55,8 @@ class MainActivity : ComponentActivity() {
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
Scaffold( Scaffold(
bottomBar = { BottomBar(navController) }, bottomBar = { BottomBar(navController) },
snackbarHost = { SnackbarHost(snackbarHostState) } snackbarHost = { SnackbarHost(snackbarHostState) },
contentWindowInsets = WindowInsets(0, 0, 0, 0)
) { innerPadding -> ) { innerPadding ->
CompositionLocalProvider( CompositionLocalProvider(
LocalSnackbarHost provides snackbarHostState, LocalSnackbarHost provides snackbarHostState,
@@ -61,9 +75,13 @@ class MainActivity : ComponentActivity() {
@Composable @Composable
private fun BottomBar(navController: NavHostController) { private fun BottomBar(navController: NavHostController) {
val navigator = navController.rememberDestinationsNavigator()
val isManager = Natives.becomeManager(ksuApp.packageName) val isManager = Natives.becomeManager(ksuApp.packageName)
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable() 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 -> BottomBarDestination.entries.forEach { destination ->
if (!fullFeatured && destination.rootRequired) return@forEach if (!fullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
@@ -71,11 +89,10 @@ private fun BottomBar(navController: NavHostController) {
selected = isCurrentDestOnBackStack, selected = isCurrentDestOnBackStack,
onClick = { onClick = {
if (isCurrentDestOnBackStack) { if (isCurrentDestOnBackStack) {
navController.popBackStack(destination.direction, false) navigator.popBackStack(destination.direction, false)
} }
navigator.navigate(destination.direction) {
navController.navigate(destination.direction.route) { popUpTo(NavGraphs.root) {
popUpTo(NavGraphs.root.route) {
saveState = true saveState = true
} }
launchSingleTop = true launchSingleTop = true

View File

@@ -1,6 +1,7 @@
package me.weishu.kernelsu.ui.component package me.weishu.kernelsu.ui.component
import android.graphics.text.LineBreaker import android.graphics.text.LineBreaker
import android.os.Build
import android.os.Parcelable import android.os.Parcelable
import android.text.Layout import android.text.Layout
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
@@ -96,8 +97,8 @@ interface ConfirmDialogHandle : DialogHandle {
} }
private abstract class DialogHandleBase( private abstract class DialogHandleBase(
protected val visible: MutableState<Boolean>, val visible: MutableState<Boolean>,
protected val coroutineScope: CoroutineScope val coroutineScope: CoroutineScope
) : DialogHandle { ) : DialogHandle {
override val isShown: Boolean override val isShown: Boolean
get() = visible.value get() = visible.value
@@ -432,7 +433,9 @@ private fun MarkdownContent(content: String) {
TextView(context).apply { TextView(context).apply {
movementMethod = LinkMovementMethod.getInstance() movementMethod = LinkMovementMethod.getInstance()
setSpannableFactory(NoCopySpannableFactory.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 hyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE
layoutParams = ViewGroup.LayoutParams( layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT

View File

@@ -5,8 +5,12 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box 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.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -132,7 +136,8 @@ fun SearchAppBar(
dropdownContent() dropdownContent()
} }
} },
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) )
} }

View File

@@ -1,11 +1,10 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package me.weishu.kernelsu.ui.component.profile package me.weishu.kernelsu.ui.component.profile
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
@@ -20,12 +19,14 @@ import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.OutlinedCard import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -86,7 +87,7 @@ fun RootProfileConfig(
) { ) {
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier
.menuAnchor() .menuAnchor(MenuAnchorType.PrimaryNotEditable)
.fillMaxWidth(), .fillMaxWidth(),
readOnly = true, readOnly = true,
label = { Text(stringResource(R.string.profile_namespace)) }, label = { Text(stringResource(R.string.profile_namespace)) },
@@ -184,7 +185,7 @@ fun RootProfileConfig(
} }
} }
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable @Composable
fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>) -> Unit) { fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>) -> Unit) {
val selectGroupsDialog = rememberCustomDialog { dismiss: () -> Unit -> val selectGroupsDialog = rememberCustomDialog { dismiss: () -> Unit ->
@@ -234,14 +235,20 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
) )
} }
OutlinedCard(modifier = Modifier OutlinedCard(
.fillMaxWidth() modifier = Modifier
.padding(16.dp) .fillMaxWidth()
.clickable { .padding(16.dp)
selectGroupsDialog.show() ) {
}) {
Column(modifier = Modifier.padding(16.dp)) { Column(
modifier = Modifier
.fillMaxSize()
.clickable {
selectGroupsDialog.show()
}
.padding(16.dp)
) {
Text(stringResource(R.string.profile_groups)) Text(stringResource(R.string.profile_groups))
FlowRow { FlowRow {
selected.forEach { group -> selected.forEach { group ->
@@ -256,7 +263,7 @@ fun GroupsPanel(selected: List<Groups>, closeSelection: (selection: Set<Groups>)
} }
} }
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
@Composable @Composable
fun CapsPanel( fun CapsPanel(
selected: Collection<Capabilities>, selected: Collection<Capabilities>,
@@ -299,14 +306,20 @@ fun CapsPanel(
) )
} }
OutlinedCard(modifier = Modifier OutlinedCard(
.fillMaxWidth() modifier = Modifier
.padding(16.dp) .fillMaxWidth()
.clickable { .padding(16.dp)
selectCapabilitiesDialog.show() ) {
}) {
Column(modifier = Modifier.padding(16.dp)) { Column(
modifier = Modifier
.fillMaxSize()
.clickable {
selectCapabilitiesDialog.show()
}
.padding(16.dp)
) {
Text(stringResource(R.string.profile_capabilities)) Text(stringResource(R.string.profile_capabilities))
FlowRow { FlowRow {
selected.forEach { group -> selected.forEach { group ->
@@ -329,10 +342,10 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
mutableStateOf(false) mutableStateOf(false)
} }
var lastValidUid by remember { var lastValidUid by remember {
mutableStateOf(uid) mutableIntStateOf(uid)
} }
val keyboardController = LocalSoftwareKeyboardController.current val keyboardController = LocalSoftwareKeyboardController.current
OutlinedTextField( OutlinedTextField(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
label = { Text(label) }, label = { Text(label) },
@@ -365,6 +378,7 @@ private fun UidPanel(uid: Int, label: String, onUidChange: (Int) -> Unit) {
}) })
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun SELinuxPanel( private fun SELinuxPanel(
profile: Natives.Profile, profile: Natives.Profile,
@@ -452,7 +466,7 @@ private fun SELinuxPanel(
), ),
label = { Text(text = stringResource(R.string.profile_selinux_context)) }, label = { Text(text = stringResource(R.string.profile_selinux_context)) },
value = profile.context, value = profile.context,
onValueChange = { }, onValueChange = { }
) )
}) })
} }

View File

@@ -12,6 +12,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -54,7 +55,7 @@ fun TemplateConfig(
) { ) {
OutlinedTextField( OutlinedTextField(
modifier = Modifier modifier = Modifier
.menuAnchor() .menuAnchor(MenuAnchorType.PrimaryNotEditable)
.fillMaxWidth(), .fillMaxWidth(),
readOnly = true, readOnly = true,
label = { Text(stringResource(R.string.profile_template)) }, label = { Text(stringResource(R.string.profile_template)) },

View File

@@ -7,10 +7,14 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row 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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@@ -50,6 +54,9 @@ import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import com.ramcosta.composedestinations.annotation.Destination 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 com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.weishu.kernelsu.Natives 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.AppProfileConfig
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
import me.weishu.kernelsu.ui.component.profile.TemplateConfig 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.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.forceStopApp import me.weishu.kernelsu.ui.util.forceStopApp
import me.weishu.kernelsu.ui.util.getSepolicy import me.weishu.kernelsu.ui.util.getSepolicy
@@ -73,7 +78,7 @@ import me.weishu.kernelsu.ui.viewmodel.getTemplateInfoById
* @author weishu * @author weishu
* @date 2023/5/16. * @date 2023/5/16.
*/ */
@Destination @Destination<RootGraph>
@Composable @Composable
fun AppProfileScreen( fun AppProfileScreen(
navigator: DestinationsNavigator, navigator: DestinationsNavigator,
@@ -82,10 +87,8 @@ fun AppProfileScreen(
val context = LocalContext.current val context = LocalContext.current
val snackbarHost = LocalSnackbarHost.current val snackbarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val failToUpdateAppProfile = val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label)
stringResource(R.string.failed_to_update_app_profile).format(appInfo.label) val failToUpdateSepolicy = stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label)
val failToUpdateSepolicy =
stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label)
val packageName = appInfo.packageName val packageName = appInfo.packageName
val initialProfile = Natives.getAppProfile(packageName, appInfo.uid) val initialProfile = Natives.getAppProfile(packageName, appInfo.uid)
@@ -98,6 +101,7 @@ fun AppProfileScreen(
Scaffold( Scaffold(
topBar = { TopBar { navigator.popBackStack() } }, topBar = { TopBar { navigator.popBackStack() } },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { paddingValues -> ) { paddingValues ->
AppProfileInner( AppProfileInner(
modifier = Modifier modifier = Modifier
@@ -248,6 +252,7 @@ private fun TopBar(onBack: () -> Unit) {
onClick = onBack onClick = onBack
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
}, },
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) )
} }

View File

@@ -5,11 +5,11 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.outlined.*
import androidx.compose.ui.graphics.vector.ImageVector 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 com.ramcosta.composedestinations.spec.DirectionDestinationSpec
import me.weishu.kernelsu.R 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( enum class BottomBarDestination(
val direction: DirectionDestinationSpec, val direction: DirectionDestinationSpec,

View File

@@ -4,8 +4,12 @@ import android.net.Uri
import android.os.Environment import android.os.Environment
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.foundation.layout.Column 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.fillMaxSize
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons 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.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -66,7 +71,7 @@ enum class FlashingStatus {
* @date 2023/1/1. * @date 2023/1/1.
*/ */
@Composable @Composable
@Destination @Destination<RootGraph>
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
var text by rememberSaveable { mutableStateOf("") } var text by rememberSaveable { mutableStateOf("") }
@@ -139,8 +144,8 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
text = { Text(text = reboot) }, text = { Text(text = reboot) },
) )
} }
},
} contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { innerPadding -> ) { innerPadding ->
KeyEventBlocker { KeyEventBlocker {
it.key == Key.VolumeDown || it.key == Key.VolumeUp it.key == Key.VolumeDown || it.key == Key.VolumeUp
@@ -227,7 +232,8 @@ private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -
contentDescription = "Localized description" contentDescription = "Localized description"
) )
} }
} },
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) )
} }

View File

@@ -27,32 +27,35 @@ import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.pm.PackageInfoCompat
import com.ramcosta.composedestinations.annotation.Destination 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 com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import me.weishu.kernelsu.* import me.weishu.kernelsu.*
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.rememberConfirmDialog 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.*
import me.weishu.kernelsu.ui.util.module.LatestVersionInfo import me.weishu.kernelsu.ui.util.module.LatestVersionInfo
@RootNavGraph(start = true) @Destination<RootGraph>(start = true)
@Destination
@Composable @Composable
fun HomeScreen(navigator: DestinationsNavigator) { fun HomeScreen(navigator: DestinationsNavigator) {
val kernelVersion = getKernelVersion() val kernelVersion = getKernelVersion()
Scaffold(topBar = { Scaffold(
TopBar(kernelVersion, onSettingsClick = { topBar = {
navigator.navigate(SettingScreenDestination) TopBar(kernelVersion, onSettingsClick = {
}, onInstallClick = { navigator.navigate(SettingScreenDestination)
navigator.navigate(InstallScreenDestination) }, onInstallClick = {
}) navigator.navigate(InstallScreenDestination)
}) { innerPadding -> })
},
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { innerPadding ->
Column( Column(
modifier = Modifier modifier = Modifier
.padding(innerPadding) .padding(innerPadding)
@@ -103,12 +106,11 @@ fun UpdateCard() {
val context = LocalContext.current val context = LocalContext.current
val latestVersionInfo = LatestVersionInfo() val latestVersionInfo = LatestVersionInfo()
val newVersion by produceState(initialValue = latestVersionInfo) { val newVersion by produceState(initialValue = latestVersionInfo) {
value = withContext(Dispatchers.IO){ value = withContext(Dispatchers.IO) {
checkNewVersion() checkNewVersion()
} }
} }
val currentVersionCode = getManagerVersion(context).second val currentVersionCode = getManagerVersion(context).second
val newVersionCode = newVersion.versionCode val newVersionCode = newVersion.versionCode
val newVersionUrl = newVersion.downloadUrl val newVersionUrl = newVersion.downloadUrl
@@ -158,50 +160,54 @@ private fun TopBar(
onInstallClick: () -> Unit, onInstallClick: () -> Unit,
onSettingsClick: () -> Unit onSettingsClick: () -> Unit
) { ) {
TopAppBar(title = { Text(stringResource(R.string.app_name)) }, actions = { TopAppBar(
if (kernelVersion.isGKI()) { title = { Text(stringResource(R.string.app_name)) },
IconButton(onClick = onInstallClick) { 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( Icon(
imageVector = Icons.Filled.Archive, imageVector = Icons.Filled.Refresh,
contentDescription = stringResource(id = R.string.install) 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)
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)
)
}
})
} }
@Composable @Composable
@@ -415,9 +421,10 @@ private fun InfoCard() {
} }
} }
fun getManagerVersion(context: Context): Pair<String, Int> { fun getManagerVersion(context: Context): Pair<String, Long> {
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)!! 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 @Preview

View File

@@ -9,8 +9,12 @@ import androidx.annotation.StringRes
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row 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.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.FileUpload 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.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection import com.maxkeppeler.sheets.list.models.ListSelection
import com.ramcosta.composedestinations.annotation.Destination 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.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.DialogHandle import me.weishu.kernelsu.ui.component.DialogHandle
import me.weishu.kernelsu.ui.component.rememberConfirmDialog import me.weishu.kernelsu.ui.component.rememberConfirmDialog
import me.weishu.kernelsu.ui.component.rememberCustomDialog 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.LkmSelection
import me.weishu.kernelsu.ui.util.getCurrentKmi import me.weishu.kernelsu.ui.util.getCurrentKmi
import me.weishu.kernelsu.ui.util.getSupportedKmis import me.weishu.kernelsu.ui.util.getSupportedKmis
@@ -58,7 +63,7 @@ import me.weishu.kernelsu.ui.util.rootAvailable
* @author weishu * @author weishu
* @date 2024/3/12. * @date 2024/3/12.
*/ */
@Destination @Destination<RootGraph>
@Composable @Composable
fun InstallScreen(navigator: DestinationsNavigator) { fun InstallScreen(navigator: DestinationsNavigator) {
var installMethod by remember { var installMethod by remember {
@@ -113,11 +118,14 @@ fun InstallScreen(navigator: DestinationsNavigator) {
}) })
} }
Scaffold(topBar = { Scaffold(
TopBar( topBar = {
onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload TopBar(
) onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload
}) { )
},
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) {
Column(modifier = Modifier.padding(it)) { Column(modifier = Modifier.padding(it)) {
SelectInstallMethod { method -> SelectInstallMethod { method ->
installMethod = method installMethod = method
@@ -293,15 +301,18 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) { private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) {
TopAppBar(title = { Text(stringResource(R.string.install)) }, navigationIcon = { TopAppBar(
IconButton( title = { Text(stringResource(R.string.install)) }, navigationIcon = {
onClick = onBack IconButton(
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } onClick = onBack
}, actions = { ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
IconButton(onClick = onLkmUpload) { }, actions = {
Icon(Icons.Filled.FileUpload, contentDescription = null) IconButton(onClick = onLkmUpload) {
} Icon(Icons.Filled.FileUpload, contentDescription = null)
}) }
},
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
)
} }
@Composable @Composable

View File

@@ -14,11 +14,15 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer 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.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape 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.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.ExtendedFloatingActionButton
@@ -64,6 +67,8 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.ramcosta.composedestinations.annotation.Destination 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.DestinationsNavigator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch 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.ConfirmResult
import me.weishu.kernelsu.ui.component.rememberConfirmDialog import me.weishu.kernelsu.ui.component.rememberConfirmDialog
import me.weishu.kernelsu.ui.component.rememberLoadingDialog 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.DownloadListener
import me.weishu.kernelsu.ui.util.LocalSnackbarHost import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.download 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 me.weishu.kernelsu.ui.webui.WebUIActivity
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@Destination @Destination<RootGraph>
@Composable @Composable
fun ModuleScreen(navigator: DestinationsNavigator) { fun ModuleScreen(navigator: DestinationsNavigator) {
val viewModel = viewModel<ModuleViewModel>() val viewModel = viewModel<ModuleViewModel>()
@@ -102,41 +106,46 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
val hideInstallButton = isSafeMode || hasMagisk val hideInstallButton = isSafeMode || hasMagisk
Scaffold(topBar = { Scaffold(
TopBar() topBar = {
}, floatingActionButton = if (hideInstallButton) { TopBar()
{ /* Empty */ } },
} else { floatingActionButton = {
{ if (hideInstallButton) {
val moduleInstall = stringResource(id = R.string.module_install) /* Empty */
val selectZipLauncher = rememberLauncherForActivityResult( } else {
contract = ActivityResultContracts.StartActivityForResult() val moduleInstall = stringResource(id = R.string.module_install)
) { val selectZipLauncher = rememberLauncherForActivityResult(
if (it.resultCode != RESULT_OK) { contract = ActivityResultContracts.StartActivityForResult()
return@rememberLauncherForActivityResult ) {
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( contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
onClick = { ) { innerPadding ->
// 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 ->
when { when {
hasMagisk -> { hasMagisk -> {
@@ -163,10 +172,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it))) navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it)))
}, onClickModule = { id, name, hasWebUi -> }, onClickModule = { id, name, hasWebUi ->
if (hasWebUi) { if (hasWebUi) {
context.startActivity(Intent(context, WebUIActivity::class.java) context.startActivity(
.setData(Uri.parse("kernelsu://webui/$id")) Intent(context, WebUIActivity::class.java)
.putExtra("id", id) .setData(Uri.parse("kernelsu://webui/$id"))
.putExtra("name", name) .putExtra("id", id)
.putExtra("name", name)
) )
} }
}) })
@@ -419,7 +429,10 @@ private fun ModuleList(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
private fun TopBar() { 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 @Composable
@@ -433,13 +446,16 @@ private fun ModuleItem(
onClick: (ModuleViewModel.ModuleInfo) -> Unit onClick: (ModuleViewModel.ModuleInfo) -> Unit
) { ) {
ElevatedCard( ElevatedCard(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth()
colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surface)
) { ) {
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough 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( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,

View File

@@ -1,18 +1,18 @@
package me.weishu.kernelsu.ui.screen package me.weishu.kernelsu.ui.screen
import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.OpenableColumns
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row 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.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons 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.Save
import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Share
import androidx.compose.material.icons.filled.Update import androidx.compose.material.icons.filled.Update
import androidx.compose.material3.BottomSheetScaffold
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton 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.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection import com.maxkeppeler.sheets.list.models.ListSelection
import com.ramcosta.composedestinations.annotation.Destination 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.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import kotlinx.coroutines.Dispatchers 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.rememberConfirmDialog
import me.weishu.kernelsu.ui.component.rememberCustomDialog import me.weishu.kernelsu.ui.component.rememberCustomDialog
import me.weishu.kernelsu.ui.component.rememberLoadingDialog 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.getBugreportFile
import me.weishu.kernelsu.ui.util.getFileNameFromUri import me.weishu.kernelsu.ui.util.getFileNameFromUri
import me.weishu.kernelsu.ui.util.shrinkModules import me.weishu.kernelsu.ui.util.shrinkModules
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
/** /**
* @author weishu * @author weishu
* @date 2023/1/1. * @date 2023/1/1.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Destination @Destination<RootGraph>
@Composable @Composable
fun SettingScreen(navigator: DestinationsNavigator) { fun SettingScreen(navigator: DestinationsNavigator) {
Scaffold( Scaffold(
@@ -98,7 +96,8 @@ fun SettingScreen(navigator: DestinationsNavigator) {
TopBar(onBack = { TopBar(onBack = {
navigator.popBackStack() navigator.popBackStack()
}) })
} },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { paddingValues -> ) { paddingValues ->
val aboutDialog = rememberCustomDialog { val aboutDialog = rememberCustomDialog {
AboutDialog(it) AboutDialog(it)
@@ -184,17 +183,20 @@ fun SettingScreen(navigator: DestinationsNavigator) {
showBottomsheet = true showBottomsheet = true
} }
) )
if (showBottomsheet){ if (showBottomsheet) {
ModalBottomSheet( ModalBottomSheet(
onDismissRequest = { showBottomsheet = false }, onDismissRequest = { showBottomsheet = false },
content = { content = {
Row(modifier = Modifier.padding(10.dp) Row(
.align(Alignment.CenterHorizontally) modifier = Modifier
.padding(10.dp)
.align(Alignment.CenterHorizontally)
) { ) {
Box{ Box {
Column( Column(
modifier = Modifier.padding(16.dp) modifier = Modifier
.padding(16.dp)
.clickable { .clickable {
scope.launch { scope.launch {
val bugreport = loadingDialog.withLoading { val bugreport = loadingDialog.withLoading {
@@ -209,14 +211,15 @@ fun SettingScreen(navigator: DestinationsNavigator) {
"${BuildConfig.APPLICATION_ID}.fileprovider", "${BuildConfig.APPLICATION_ID}.fileprovider",
bugreport bugreport
) )
val filename = getFileNameFromUri(context , uri) val filename = getFileNameFromUri(context, uri)
val savefile = Intent(Intent.ACTION_CREATE_DOCUMENT).apply { val savefile =
addCategory(Intent.CATEGORY_OPENABLE) Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
type = "application/zip" addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_STREAM, uri) type = "application/zip"
putExtra(Intent.EXTRA_TITLE, filename) putExtra(Intent.EXTRA_STREAM, uri)
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION putExtra(Intent.EXTRA_TITLE, filename)
} flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
}
context.startActivity( context.startActivity(
Intent.createChooser( Intent.createChooser(
savefile, savefile,
@@ -245,9 +248,10 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
} }
Box{ Box {
Column( Column(
modifier = Modifier.padding(16.dp) modifier = Modifier
.padding(16.dp)
.clickable { .clickable {
scope.launch { scope.launch {
val bugreport = loadingDialog.withLoading { val bugreport = loadingDialog.withLoading {
@@ -350,6 +354,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
} }
} }
} }
@Composable @Composable
fun UninstallItem( fun UninstallItem(
navigator: DestinationsNavigator, navigator: DestinationsNavigator,
@@ -374,11 +379,9 @@ fun UninstallItem(
UninstallType.PERMANENT -> navigator.navigate( UninstallType.PERMANENT -> navigator.navigate(
FlashScreenDestination(FlashIt.FlashUninstall) FlashScreenDestination(FlashIt.FlashUninstall)
) )
UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate( UninstallType.RESTORE_STOCK_IMAGE -> navigator.navigate(
FlashScreenDestination(FlashIt.FlashRestore) FlashScreenDestination(FlashIt.FlashRestore)
) )
UninstallType.NONE -> Unit UninstallType.NONE -> Unit
} }
} }
@@ -464,6 +467,7 @@ private fun TopBar(onBack: () -> Unit = {}) {
onClick = onBack onClick = onBack
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
}, },
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) )
} }

View File

@@ -26,16 +26,17 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import com.ramcosta.composedestinations.annotation.Destination 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 com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.weishu.kernelsu.Natives import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.SearchAppBar import me.weishu.kernelsu.ui.component.SearchAppBar
import me.weishu.kernelsu.ui.screen.destinations.AppProfileScreenDestination
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Destination @Destination<RootGraph>
@Composable @Composable
fun SuperUserScreen(navigator: DestinationsNavigator) { fun SuperUserScreen(navigator: DestinationsNavigator) {
val viewModel = viewModel<SuperUserViewModel>() val viewModel = viewModel<SuperUserViewModel>()
@@ -92,7 +93,8 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
} }
}, },
) )
} },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { innerPadding -> ) { innerPadding ->
val refreshState = rememberPullRefreshState( val refreshState = rememberPullRefreshState(
refreshing = viewModel.isRefreshing, refreshing = viewModel.isRefreshing,

View File

@@ -7,8 +7,12 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues 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.fillMaxSize
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ExperimentalMaterialApi
@@ -47,13 +51,14 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.ramcosta.composedestinations.annotation.Destination 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.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.ResultRecipient import com.ramcosta.composedestinations.result.ResultRecipient
import com.ramcosta.composedestinations.result.getOr import com.ramcosta.composedestinations.result.getOr
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.screen.destinations.TemplateEditorScreenDestination
import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel
/** /**
@@ -62,7 +67,7 @@ import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel
*/ */
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Destination @Destination<RootGraph>
@Composable @Composable
fun AppProfileTemplateScreen( fun AppProfileTemplateScreen(
navigator: DestinationsNavigator, navigator: DestinationsNavigator,
@@ -141,6 +146,7 @@ fun AppProfileTemplateScreen(
text = { Text(stringResource(id = R.string.app_profile_template_create)) }, text = { Text(stringResource(id = R.string.app_profile_template_create)) },
) )
}, },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { innerPadding -> ) { innerPadding ->
val refreshState = rememberPullRefreshState( val refreshState = rememberPullRefreshState(
refreshing = viewModel.isRefreshing, refreshing = viewModel.isRefreshing,
@@ -254,6 +260,7 @@ private fun TopBar(
}) })
} }
} }
} },
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) )
} }

View File

@@ -3,8 +3,12 @@ package me.weishu.kernelsu.ui.screen
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column 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.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions 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.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.result.ResultBackNavigator import com.ramcosta.composedestinations.result.ResultBackNavigator
import me.weishu.kernelsu.Natives import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.R import me.weishu.kernelsu.R
@@ -52,7 +57,7 @@ import me.weishu.kernelsu.ui.viewmodel.toJSON
* @date 2023/10/20. * @date 2023/10/20.
*/ */
@OptIn(ExperimentalComposeUiApi::class) @OptIn(ExperimentalComposeUiApi::class)
@Destination @Destination<RootGraph>
@Composable @Composable
fun TemplateEditorScreen( fun TemplateEditorScreen(
navigator: ResultBackNavigator<Boolean>, navigator: ResultBackNavigator<Boolean>,
@@ -108,6 +113,7 @@ fun TemplateEditorScreen(
} }
}) })
}, },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
) { innerPadding -> ) { innerPadding ->
Column( Column(
modifier = Modifier modifier = Modifier
@@ -242,37 +248,40 @@ private fun TopBar(
onDelete: () -> Unit = {}, onDelete: () -> Unit = {},
onSave: () -> Unit = {} onSave: () -> Unit = {}
) { ) {
TopAppBar(title = { TopAppBar(
Column { title = {
Text(title) Column {
if (summary.isNotBlank()) { Text(title)
Text( if (summary.isNotBlank()) {
text = summary, Text(
style = MaterialTheme.typography.bodyMedium, 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)
) )
} }
} IconButton(onClick = onSave) {
}, navigationIcon = { Icon(
IconButton( imageVector = Icons.Filled.Save,
onClick = onBack contentDescription = stringResource(id = R.string.app_profile_template_save)
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } )
}, actions = { }
if (readOnly) { },
return@TopAppBar windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
} )
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)
)
}
})
} }
@Composable @Composable
@@ -289,17 +298,16 @@ private fun TextEdit(
value = text, value = text,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
label = { Text(label) }, label = { Text(label) },
suffix = suffix = {
if (errorHint.isNotBlank()) { if (errorHint.isNotBlank()) {
{
Text( Text(
text = if (isError) errorHint else "", text = if (isError) errorHint else "",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error color = MaterialTheme.colorScheme.error
) )
} else {
null
} }
} else {
null
}, },
isError = isError, isError = isError,
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(

View File

@@ -7,12 +7,8 @@ import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import com.google.accompanist.systemuicontroller.rememberSystemUiController
private val DarkColorScheme = darkColorScheme( private val DarkColorScheme = darkColorScheme(
primary = YELLOW, primary = YELLOW,
@@ -42,20 +38,6 @@ fun KernelSUTheme(
else -> LightColorScheme 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( MaterialTheme(
colorScheme = colorScheme, colorScheme = colorScheme,
typography = Typography, typography = Typography,

View File

@@ -1,16 +1,12 @@
package me.weishu.kernelsu.ui.util package me.weishu.kernelsu.ui.util
import android.content.ContentResolver
import android.content.Context import android.content.Context
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.ParcelFileDescriptor
import android.system.Os import android.system.Os
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import me.weishu.kernelsu.Natives import me.weishu.kernelsu.Natives
import me.weishu.kernelsu.ui.screen.getManagerVersion import me.weishu.kernelsu.ui.screen.getManagerVersion
import java.io.File import java.io.File
import java.io.FileOutputStream
import java.io.FileWriter import java.io.FileWriter
import java.io.PrintWriter import java.io.PrintWriter
import java.time.LocalDateTime import java.time.LocalDateTime

View File

@@ -1,7 +1,7 @@
package me.weishu.kernelsu.ui.util package me.weishu.kernelsu.ui.util
import androidx.compose.ui.res.stringResource
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import me.weishu.kernelsu.R import me.weishu.kernelsu.R

View File

@@ -2,13 +2,18 @@ package me.weishu.kernelsu.ui.webui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ActivityManager import android.app.ActivityManager
import android.content.Context import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.ViewGroup.MarginLayoutParams
import android.webkit.WebResourceRequest import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse import android.webkit.WebResourceResponse
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import androidx.activity.ComponentActivity 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 androidx.webkit.WebViewAssetLoader
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import me.weishu.kernelsu.ui.util.createRootShell import me.weishu.kernelsu.ui.util.createRootShell
@@ -21,12 +26,26 @@ class WebUIActivity : ComponentActivity() {
private var rootShell: Shell? = null private var rootShell: Shell? = null
override fun onCreate(savedInstanceState: Bundle?) { 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) super.onCreate(savedInstanceState)
val moduleId = intent.getStringExtra("id")!! val moduleId = intent.getStringExtra("id")!!
val name = intent.getStringExtra("name")!! 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)) WebView.setWebContentsDebuggingEnabled(prefs.getBoolean("enable_web_debugging", false))
val moduleDir = "/data/adb/modules/${moduleId}" val moduleDir = "/data/adb/modules/${moduleId}"
@@ -50,6 +69,16 @@ class WebUIActivity : ComponentActivity() {
} }
val webView = WebView(this).apply { val webView = WebView(this).apply {
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updateLayoutParams<MarginLayoutParams> {
leftMargin = inset.left
rightMargin = inset.right
topMargin = inset.top
bottomMargin = inset.bottom
}
return@setOnApplyWindowInsetsListener insets
}
settings.javaScriptEnabled = true settings.javaScriptEnabled = true
settings.domStorageEnabled = true settings.domStorageEnabled = true
settings.allowFileAccess = false settings.allowFileAccess = false

View File

@@ -9,21 +9,24 @@ import android.view.Window
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import android.webkit.WebView import android.webkit.WebView
import android.widget.Toast import android.widget.Toast
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import com.topjohnwu.superuser.CallbackList import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.internal.UiThreadHandler 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.createRootShell
import me.weishu.kernelsu.ui.util.listModules
import me.weishu.kernelsu.ui.util.withNewRootShell import me.weishu.kernelsu.ui.util.withNewRootShell
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import java.util.concurrent.CompletableFuture
import java.io.File 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 @JavascriptInterface
fun exec(cmd: String): String { fun exec(cmd: String): String {
@@ -187,28 +190,20 @@ class WebViewInterface(val context: Context, private val webView: WebView, priva
} }
var keys = currentInfo.keys() var keys = currentInfo.keys()
for(key in keys) { for (key in keys) {
currentModuleInfo.put(key, currentInfo.get(key)); currentModuleInfo.put(key, currentInfo.get(key))
} }
break; break
} }
return currentModuleInfo.toString(); return currentModuleInfo.toString()
} }
} }
fun hideSystemUI(window: Window) { fun hideSystemUI(window: Window) =
WindowCompat.setDecorFitsSystemWindows(window, false)
WindowInsetsControllerCompat(window, window.decorView).let { controller -> WindowInsetsControllerCompat(window, window.decorView).let { controller ->
controller.hide(WindowInsetsCompat.Type.systemBars()) controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior = controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
} }
}
fun showSystemUI(window: Window) { fun showSystemUI(window: Window) =
WindowCompat.setDecorFitsSystemWindows(window, true) WindowInsetsControllerCompat(window, window.decorView).show(WindowInsetsCompat.Type.systemBars())
WindowInsetsControllerCompat(
window,
window.decorView
).show(WindowInsetsCompat.Type.systemBars())
}

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.KernelSU" parent="android:Theme.Material.NoActionBar">
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
</resources>

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.KernelSU" parent="android:Theme.Material.NoActionBar" /> <style name="Theme.KernelSU" parent="android:Theme.Material.NoActionBar">
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="o_mr1">shortEdges</item>
<style name="Theme.KernelSU.WebUI" parent="Theme.KernelSU">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">false</item>
</style> </style>
<style name="Theme.KernelSU.WebUI" parent="Theme.KernelSU" />
</resources> </resources>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.KernelSU" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
</resources>

View File

@@ -1,10 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.KernelSU" parent="android:Theme.Material.Light.NoActionBar" /> <style name="Theme.KernelSU" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="o_mr1">shortEdges</item>
</style>
<style name="Theme.KernelSU.WebUI" parent="Theme.KernelSU"> <style name="Theme.KernelSU.WebUI" parent="Theme.KernelSU">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowLightStatusBar">true</item> <item name="android:windowLightStatusBar">true</item>
<item name="android:windowLightNavigationBar" tools:targetApi="o_mr1">true</item>
</style> </style>
</resources> </resources>

View File

@@ -1,18 +1,18 @@
[versions] [versions]
agp = "8.5.2" agp = "8.6.1"
kotlin = "2.0.20" kotlin = "2.0.20"
ksp = "2.0.20-1.0.24" ksp = "2.0.20-1.0.25"
compose-bom = "2024.08.00" compose-bom = "2024.09.02"
lifecycle = "2.8.4" lifecycle = "2.8.6"
accompanist = "0.34.0" accompanist = "0.36.0"
navigation = "2.7.7" navigation = "2.8.1"
activity-compose = "1.9.1" activity-compose = "1.9.2"
kotlinx-coroutines = "1.8.1" kotlinx-coroutines = "1.9.0"
coil-compose = "2.7.0" coil-compose = "2.7.0"
compose-destination = "1.10.2" compose-destination = "2.1.0-beta12"
sheets-compose-dialogs = "1.3.0" sheets-compose-dialogs = "1.3.0"
markdown = "4.6.2" markdown = "4.6.2"
webkit = "1.11.0" webkit = "1.12.0"
appiconloader-coil = "1.5.0" appiconloader-coil = "1.5.0"
parcelablelist = "2.0.1" parcelablelist = "2.0.1"
libsu = "6.0.0" 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-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-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-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" } 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" } 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" } 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" } sheet-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs", name = "core", version.ref = "sheets-compose-dialogs" }