You've already forked KernelSU
mirror of
https://github.com/tiann/KernelSU.git
synced 2025-08-27 23:46:34 +00:00
manager: Refactor the click logic of ModuleItem (#2105)
Drop `com.google.accompanist` that we needn't it Remove unused metadata, abi Optimize app icon (No visual changes) Update Gradle to 8.10.2 Enable per app language support Optimize `SwitchItem` https://github.com/user-attachments/assets/777729e6-5108-4060-91a7-28b5b9d98441 Refactor the click logic of `ModuleItem` https://github.com/user-attachments/assets/e61da54a-6c1c-45d7-bf27-52b452134b7e Use compose's Text in AboutCard to support dynamicColor  Add scroll behavior for TopAppBar   Fix padding for BottomNavigationBar
This commit is contained in:
3
manager/.gitignore
vendored
3
manager/.gitignore
vendored
@@ -1,9 +1,10 @@
|
|||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
local.properties
|
|
||||||
.idea
|
.idea
|
||||||
|
.kotlin
|
||||||
.DS_Store
|
.DS_Store
|
||||||
build
|
build
|
||||||
captures
|
captures
|
||||||
.cxx
|
.cxx
|
||||||
|
local.properties
|
||||||
key.jks
|
key.jks
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
@file:Suppress("UnstableApiUsage")
|
||||||
|
|
||||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||||
|
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.agp.app)
|
alias(libs.plugins.agp.app)
|
||||||
@@ -46,7 +49,13 @@ android {
|
|||||||
useLegacyPackaging = true
|
useLegacyPackaging = true
|
||||||
}
|
}
|
||||||
resources {
|
resources {
|
||||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
// https://stackoverflow.com/a/58956288
|
||||||
|
// It will break Layout Inspector, but it's unused for release build.
|
||||||
|
excludes += "META-INF/*.version"
|
||||||
|
// https://github.com/Kotlin/kotlinx.coroutines?tab=readme-ov-file#avoiding-including-the-debug-infrastructure-in-the-resulting-apk
|
||||||
|
excludes += "DebugProbesKt.bin"
|
||||||
|
// https://issueantenna.com/repo/kotlin/kotlinx.coroutines/issues/3158
|
||||||
|
excludes += "kotlin-tooling-metadata.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +76,20 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/77745844
|
||||||
|
tasks.withType<PackageAndroidArtifact> {
|
||||||
|
doFirst { appMetadata.asFile.orNull?.writeText("") }
|
||||||
|
}
|
||||||
|
|
||||||
|
dependenciesInfo {
|
||||||
|
includeInApk = false
|
||||||
|
includeInBundle = false
|
||||||
|
}
|
||||||
|
|
||||||
|
androidResources {
|
||||||
|
generateLocaleConfig = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -87,10 +110,6 @@ dependencies {
|
|||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||||
|
|
||||||
implementation(libs.com.google.accompanist.drawablepainter)
|
|
||||||
implementation(libs.com.google.accompanist.navigation.animation)
|
|
||||||
implementation(libs.com.google.accompanist.webview)
|
|
||||||
|
|
||||||
implementation(libs.compose.destinations.core)
|
implementation(libs.compose.destinations.core)
|
||||||
ksp(libs.compose.destinations.ksp)
|
ksp(libs.compose.destinations.ksp)
|
||||||
|
|
||||||
|
|||||||
9
manager/app/proguard-rules.pro
vendored
9
manager/app/proguard-rules.pro
vendored
@@ -1,9 +0,0 @@
|
|||||||
-dontwarn org.bouncycastle.jsse.BCSSLParameters
|
|
||||||
-dontwarn org.bouncycastle.jsse.BCSSLSocket
|
|
||||||
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
|
|
||||||
-dontwarn org.conscrypt.Conscrypt$Version
|
|
||||||
-dontwarn org.conscrypt.Conscrypt
|
|
||||||
-dontwarn org.conscrypt.ConscryptHostnameVerifier
|
|
||||||
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
|
|
||||||
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
|
|
||||||
-dontwarn org.openjsse.net.ssl.OpenJSSE
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.KernelSU"
|
android:theme="@style/Theme.KernelSU"
|
||||||
tools:targetApi="34">
|
tools:targetApi="34">
|
||||||
<activity
|
<activity
|
||||||
@@ -24,13 +24,10 @@
|
|||||||
<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>
|
||||||
|
|
||||||
<meta-data
|
|
||||||
android:name="android.app.lib_name"
|
|
||||||
android:value="" />
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".ui.webui.WebUIActivity"
|
<activity
|
||||||
|
android:name=".ui.webui.WebUIActivity"
|
||||||
android:autoRemoveFromRecents="true"
|
android:autoRemoveFromRecents="true"
|
||||||
android:documentLaunchMode="intoExisting"
|
android:documentLaunchMode="intoExisting"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import androidx.activity.compose.setContent
|
|||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.displayCutout
|
||||||
import androidx.compose.foundation.layout.only
|
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.systemBars
|
||||||
|
import androidx.compose.foundation.layout.union
|
||||||
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
|
||||||
@@ -80,7 +82,9 @@ private fun BottomBar(navController: NavHostController) {
|
|||||||
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
||||||
NavigationBar(
|
NavigationBar(
|
||||||
tonalElevation = 8.dp,
|
tonalElevation = 8.dp,
|
||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Bottom + WindowInsetsSides.Horizontal)
|
windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only(
|
||||||
|
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
BottomBarDestination.entries.forEach { destination ->
|
BottomBarDestination.entries.forEach { destination ->
|
||||||
if (!fullFeatured && destination.rootRequired) return@forEach
|
if (!fullFeatured && destination.rootRequired) return@forEach
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package me.weishu.kernelsu.ui.component
|
package me.weishu.kernelsu.ui.component
|
||||||
|
|
||||||
import android.text.method.LinkMovementMethod
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -11,24 +9,28 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.LocalContentColor
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.draw.scale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.res.colorResource
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.TextLinkStyles
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.fromHtml
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
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.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.core.content.res.ResourcesCompat
|
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import com.google.accompanist.drawablepainter.rememberDrawablePainter
|
|
||||||
import me.weishu.kernelsu.BuildConfig
|
import me.weishu.kernelsu.BuildConfig
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
|
|
||||||
@@ -36,9 +38,8 @@ import me.weishu.kernelsu.R
|
|||||||
@Composable
|
@Composable
|
||||||
fun AboutCard() {
|
fun AboutCard() {
|
||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth(),
|
||||||
.fillMaxWidth(),
|
shape = RoundedCornerShape(8.dp)
|
||||||
shape = RoundedCornerShape(8.dp),
|
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -52,7 +53,9 @@ fun AboutCard() {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AboutDialog(dismiss: () -> Unit) {
|
fun AboutDialog(dismiss: () -> Unit) {
|
||||||
Dialog(onDismissRequest = { dismiss() }) {
|
Dialog(
|
||||||
|
onDismissRequest = { dismiss() }
|
||||||
|
) {
|
||||||
AboutCard()
|
AboutCard()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,21 +63,20 @@ fun AboutDialog(dismiss: () -> Unit) {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun AboutCardContent() {
|
private fun AboutCardContent() {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth()
|
||||||
.fillMaxWidth()
|
|
||||||
) {
|
) {
|
||||||
val drawable = ResourcesCompat.getDrawable(
|
|
||||||
LocalContext.current.resources,
|
|
||||||
R.mipmap.ic_launcher,
|
|
||||||
LocalContext.current.theme
|
|
||||||
)
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
Image(
|
Surface(
|
||||||
painter = rememberDrawablePainter(drawable),
|
modifier = Modifier.size(40.dp),
|
||||||
contentDescription = "icon",
|
color = colorResource(id = R.color.ic_launcher_background),
|
||||||
modifier = Modifier.size(40.dp)
|
shape = CircleShape
|
||||||
)
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = R.drawable.ic_launcher_foreground),
|
||||||
|
contentDescription = "icon",
|
||||||
|
modifier = Modifier.scale(1.4f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
|
||||||
@@ -93,31 +95,31 @@ private fun AboutCardContent() {
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
HtmlText(
|
val annotatedString = AnnotatedString.Companion.fromHtml(
|
||||||
html = stringResource(
|
htmlString = stringResource(
|
||||||
id = R.string.about_source_code,
|
id = R.string.about_source_code,
|
||||||
"<b><a href=\"https://github.com/tiann/KernelSU\">GitHub</a></b>",
|
"<b><a href=\"https://github.com/tiann/KernelSU\">GitHub</a></b>",
|
||||||
"<b><a href=\"https://t.me/KernelSU\">Telegram</a></b>"
|
"<b><a href=\"https://t.me/KernelSU\">Telegram</a></b>"
|
||||||
|
),
|
||||||
|
linkStyles = TextLinkStyles(
|
||||||
|
style = SpanStyle(
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
textDecoration = TextDecoration.Underline
|
||||||
|
),
|
||||||
|
pressedStyle = SpanStyle(
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
background = MaterialTheme.colorScheme.secondaryContainer,
|
||||||
|
textDecoration = TextDecoration.Underline
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = annotatedString,
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 14.sp
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun HtmlText(html: String, modifier: Modifier = Modifier) {
|
|
||||||
val contentColor = LocalContentColor.current
|
|
||||||
AndroidView(
|
|
||||||
modifier = modifier,
|
|
||||||
factory = { context ->
|
|
||||||
TextView(context).also {
|
|
||||||
it.movementMethod = LinkMovementMethod.getInstance()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
update = {
|
|
||||||
it.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT)
|
|
||||||
it.setTextColor(contentColor.toArgb())
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,7 @@ import androidx.compose.material3.IconButton
|
|||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -52,6 +53,7 @@ fun SearchAppBar(
|
|||||||
onBackClick: (() -> Unit)? = null,
|
onBackClick: (() -> Unit)? = null,
|
||||||
onConfirm: (() -> Unit)? = null,
|
onConfirm: (() -> Unit)? = null,
|
||||||
dropdownContent: @Composable (() -> Unit)? = null,
|
dropdownContent: @Composable (() -> Unit)? = null,
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
) {
|
) {
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
@@ -137,10 +139,12 @@ fun SearchAppBar(
|
|||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun SearchAppBarPreview() {
|
private fun SearchAppBarPreview() {
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
package me.weishu.kernelsu.ui.component
|
package me.weishu.kernelsu.ui.component
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.LocalIndication
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.selection.toggleable
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SwitchItem(
|
fun SwitchItem(
|
||||||
@@ -19,10 +23,18 @@ fun SwitchItem(
|
|||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
onCheckedChange: (Boolean) -> Unit
|
onCheckedChange: (Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier
|
||||||
onCheckedChange.invoke(!checked)
|
.toggleable(
|
||||||
},
|
value = checked,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
role = Role.Switch,
|
||||||
|
enabled = enabled,
|
||||||
|
indication = LocalIndication.current,
|
||||||
|
onValueChange = onCheckedChange
|
||||||
|
),
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(title)
|
Text(title)
|
||||||
},
|
},
|
||||||
@@ -30,7 +42,12 @@ fun SwitchItem(
|
|||||||
{ Icon(icon, title) }
|
{ Icon(icon, title) }
|
||||||
},
|
},
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
Switch(checked = checked, enabled = enabled, onCheckedChange = onCheckedChange)
|
Switch(
|
||||||
|
checked = checked,
|
||||||
|
enabled = enabled,
|
||||||
|
onCheckedChange = onCheckedChange,
|
||||||
|
interactionSource = interactionSource
|
||||||
|
)
|
||||||
},
|
},
|
||||||
supportingContent = {
|
supportingContent = {
|
||||||
if (summary != null) {
|
if (summary != null) {
|
||||||
@@ -52,6 +69,6 @@ fun RadioItem(
|
|||||||
},
|
},
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
RadioButton(selected = selected, onClick = onClick)
|
RadioButton(selected = selected, onClick = onClick)
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import androidx.compose.material3.ListItem
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -43,6 +45,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
@@ -78,6 +81,7 @@ import me.weishu.kernelsu.ui.viewmodel.getTemplateInfoById
|
|||||||
* @author weishu
|
* @author weishu
|
||||||
* @date 2023/5/16.
|
* @date 2023/5/16.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@Composable
|
@Composable
|
||||||
fun AppProfileScreen(
|
fun AppProfileScreen(
|
||||||
@@ -86,6 +90,7 @@ fun AppProfileScreen(
|
|||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val snackbarHost = LocalSnackbarHost.current
|
val snackbarHost = LocalSnackbarHost.current
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).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 failToUpdateSepolicy = stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label)
|
||||||
@@ -100,19 +105,24 @@ fun AppProfileScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { TopBar { navigator.popBackStack() } },
|
topBar = {
|
||||||
|
TopBar(
|
||||||
|
onBack = { navigator.popBackStack() },
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
|
)
|
||||||
|
},
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
AppProfileInner(
|
AppProfileInner(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState()),
|
||||||
packageName = appInfo.packageName,
|
packageName = appInfo.packageName,
|
||||||
appLabel = appInfo.label,
|
appLabel = appInfo.label,
|
||||||
appIcon = {
|
appIcon = {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = ImageRequest.Builder(context).data(appInfo.packageInfo).crossfade(true)
|
model = ImageRequest.Builder(context).data(appInfo.packageInfo).crossfade(true).build(),
|
||||||
.build(),
|
|
||||||
contentDescription = appInfo.label,
|
contentDescription = appInfo.label,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(4.dp)
|
.padding(4.dp)
|
||||||
@@ -242,7 +252,10 @@ private enum class Mode(@StringRes private val res: Int) {
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(onBack: () -> Unit) {
|
private fun TopBar(
|
||||||
|
onBack: () -> Unit,
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Text(stringResource(R.string.profile))
|
Text(stringResource(R.string.profile))
|
||||||
@@ -252,7 +265,8 @@ 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)
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -34,6 +37,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.key.Key
|
import androidx.compose.ui.input.key.Key
|
||||||
import androidx.compose.ui.input.key.key
|
import androidx.compose.ui.input.key.key
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
@@ -70,6 +74,7 @@ enum class FlashingStatus {
|
|||||||
* @author weishu
|
* @author weishu
|
||||||
* @date 2023/1/1.
|
* @date 2023/1/1.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||||
@@ -81,6 +86,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
val snackBarHost = LocalSnackbarHost.current
|
val snackBarHost = LocalSnackbarHost.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
var flashing by rememberSaveable {
|
var flashing by rememberSaveable {
|
||||||
mutableStateOf(FlashingStatus.FLASHING)
|
mutableStateOf(FlashingStatus.FLASHING)
|
||||||
}
|
}
|
||||||
@@ -126,7 +132,8 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
file.writeText(logContent.toString())
|
file.writeText(logContent.toString())
|
||||||
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
@@ -154,6 +161,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize(1f)
|
.fillMaxSize(1f)
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
.verticalScroll(scrollState),
|
.verticalScroll(scrollState),
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(text) {
|
LaunchedEffect(text) {
|
||||||
@@ -207,7 +215,12 @@ fun flashIt(
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
|
private fun TopBar(
|
||||||
|
status: FlashingStatus,
|
||||||
|
onBack: () -> Unit = {},
|
||||||
|
onSave: () -> Unit = {},
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
Text(
|
Text(
|
||||||
@@ -233,7 +246,8 @@ private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.compose.runtime.*
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalUriHandler
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -41,26 +42,34 @@ import me.weishu.kernelsu.ui.component.rememberConfirmDialog
|
|||||||
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
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>(start = true)
|
@Destination<RootGraph>(start = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(navigator: DestinationsNavigator) {
|
fun HomeScreen(navigator: DestinationsNavigator) {
|
||||||
val kernelVersion = getKernelVersion()
|
val kernelVersion = getKernelVersion()
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBar(kernelVersion, onSettingsClick = {
|
TopBar(
|
||||||
navigator.navigate(SettingScreenDestination)
|
kernelVersion,
|
||||||
}, onInstallClick = {
|
onSettingsClick = {
|
||||||
navigator.navigate(InstallScreenDestination)
|
navigator.navigate(SettingScreenDestination)
|
||||||
})
|
},
|
||||||
|
onInstallClick = {
|
||||||
|
navigator.navigate(InstallScreenDestination)
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
|
)
|
||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.padding(horizontal = 16.dp)
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||||
@@ -158,7 +167,8 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") {
|
|||||||
private fun TopBar(
|
private fun TopBar(
|
||||||
kernelVersion: KernelVersion,
|
kernelVersion: KernelVersion,
|
||||||
onInstallClick: () -> Unit,
|
onInstallClick: () -> Unit,
|
||||||
onSettingsClick: () -> Unit
|
onSettingsClick: () -> Unit,
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
) {
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(stringResource(R.string.app_name)) },
|
title = { Text(stringResource(R.string.app_name)) },
|
||||||
@@ -187,8 +197,8 @@ private fun TopBar(
|
|||||||
|
|
||||||
RebootDropdownItem(id = R.string.reboot)
|
RebootDropdownItem(id = R.string.reboot)
|
||||||
|
|
||||||
val pm =
|
val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||||
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
@Suppress("DEPRECATION")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
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_userspace, reason = "userspace")
|
||||||
}
|
}
|
||||||
@@ -206,7 +216,8 @@ private fun TopBar(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import android.net.Uri
|
|||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.LocalIndication
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
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.WindowInsets
|
||||||
@@ -15,6 +16,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.only
|
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.safeDrawing
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.selection.toggleable
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
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
|
||||||
@@ -27,6 +31,9 @@ import androidx.compose.material3.RadioButton
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -35,7 +42,9 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
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.maxkeppeker.sheets.core.models.base.Header
|
import com.maxkeppeker.sheets.core.models.base.Header
|
||||||
@@ -63,6 +72,7 @@ import me.weishu.kernelsu.ui.util.rootAvailable
|
|||||||
* @author weishu
|
* @author weishu
|
||||||
* @date 2024/3/12.
|
* @date 2024/3/12.
|
||||||
*/
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@Composable
|
@Composable
|
||||||
fun InstallScreen(navigator: DestinationsNavigator) {
|
fun InstallScreen(navigator: DestinationsNavigator) {
|
||||||
@@ -118,15 +128,24 @@ fun InstallScreen(navigator: DestinationsNavigator) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBar(
|
TopBar(
|
||||||
onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload
|
onBack = { navigator.popBackStack() },
|
||||||
|
onLkmUpload = onLkmUpload,
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||||
) {
|
) { innerPadding ->
|
||||||
Column(modifier = Modifier.padding(it)) {
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(innerPadding)
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
SelectInstallMethod { method ->
|
SelectInstallMethod { method ->
|
||||||
installMethod = method
|
installMethod = method
|
||||||
}
|
}
|
||||||
@@ -239,16 +258,31 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
radioOptions.forEach { option ->
|
radioOptions.forEach { option ->
|
||||||
Row(verticalAlignment = Alignment.CenterVertically,
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.toggleable(
|
||||||
|
value = option.javaClass == selectedOption?.javaClass,
|
||||||
|
onValueChange = {
|
||||||
|
onClick(option)
|
||||||
|
},
|
||||||
|
role = Role.RadioButton,
|
||||||
|
indication = LocalIndication.current,
|
||||||
|
interactionSource = interactionSource
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
RadioButton(
|
||||||
|
selected = option.javaClass == selectedOption?.javaClass,
|
||||||
|
onClick = {
|
||||||
onClick(option)
|
onClick(option)
|
||||||
}) {
|
},
|
||||||
RadioButton(selected = option.javaClass == selectedOption?.javaClass, onClick = {
|
interactionSource = interactionSource
|
||||||
onClick(option)
|
)
|
||||||
})
|
Column(
|
||||||
Column {
|
modifier = Modifier.padding(vertical = 12.dp)
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = option.label),
|
text = stringResource(id = option.label),
|
||||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||||
@@ -300,7 +334,11 @@ 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 = {},
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(stringResource(R.string.install)) }, navigationIcon = {
|
title = { Text(stringResource(R.string.install)) }, navigationIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
@@ -311,7 +349,8 @@ private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) {
|
|||||||
Icon(Icons.Filled.FileUpload, contentDescription = null)
|
Icon(Icons.Filled.FileUpload, contentDescription = null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import android.util.Log
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.LocalIndication
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -25,6 +26,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.safeDrawing
|
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.selection.toggleable
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -45,6 +47,9 @@ import androidx.compose.material3.Switch
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -56,8 +61,10 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
@@ -89,6 +96,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
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@Composable
|
@Composable
|
||||||
fun ModuleScreen(navigator: DestinationsNavigator) {
|
fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||||
@@ -106,9 +114,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
|
|
||||||
val hideInstallButton = isSafeMode || hasMagisk
|
val hideInstallButton = isSafeMode || hasMagisk
|
||||||
|
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBar()
|
TopBar(scrollBehavior = scrollBehavior)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
if (hideInstallButton) {
|
if (hideInstallButton) {
|
||||||
@@ -146,7 +156,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
|
|
||||||
when {
|
when {
|
||||||
hasMagisk -> {
|
hasMagisk -> {
|
||||||
Box(
|
Box(
|
||||||
@@ -161,16 +170,15 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
ModuleList(
|
ModuleList(
|
||||||
viewModel = viewModel, modifier = Modifier
|
viewModel = viewModel,
|
||||||
.padding(innerPadding)
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
.fillMaxSize(),
|
boxModifier = Modifier.padding(innerPadding),
|
||||||
onInstallModule =
|
onInstallModule = {
|
||||||
{
|
|
||||||
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(
|
context.startActivity(
|
||||||
Intent(context, WebUIActivity::class.java)
|
Intent(context, WebUIActivity::class.java)
|
||||||
@@ -179,7 +187,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
.putExtra("name", name)
|
.putExtra("name", name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,6 +199,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
private fun ModuleList(
|
private fun ModuleList(
|
||||||
viewModel: ModuleViewModel,
|
viewModel: ModuleViewModel,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
boxModifier: Modifier = Modifier,
|
||||||
onInstallModule: (Uri) -> Unit,
|
onInstallModule: (Uri) -> Unit,
|
||||||
onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit
|
onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -197,12 +207,12 @@ private fun ModuleList(
|
|||||||
val failedDisable = stringResource(R.string.module_failed_to_disable)
|
val failedDisable = stringResource(R.string.module_failed_to_disable)
|
||||||
val failedUninstall = stringResource(R.string.module_uninstall_failed)
|
val failedUninstall = stringResource(R.string.module_uninstall_failed)
|
||||||
val successUninstall = stringResource(R.string.module_uninstall_success)
|
val successUninstall = stringResource(R.string.module_uninstall_success)
|
||||||
val reboot = stringResource(id = R.string.reboot)
|
val reboot = stringResource(R.string.reboot)
|
||||||
val rebootToApply = stringResource(id = R.string.reboot_to_apply)
|
val rebootToApply = stringResource(R.string.reboot_to_apply)
|
||||||
val moduleStr = stringResource(id = R.string.module)
|
val moduleStr = stringResource(R.string.module)
|
||||||
val uninstall = stringResource(id = R.string.uninstall)
|
val uninstall = stringResource(R.string.uninstall)
|
||||||
val cancel = stringResource(id = android.R.string.cancel)
|
val cancel = stringResource(android.R.string.cancel)
|
||||||
val moduleUninstallConfirm = stringResource(id = R.string.module_uninstall_confirm)
|
val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm)
|
||||||
val updateText = stringResource(R.string.module_update)
|
val updateText = stringResource(R.string.module_update)
|
||||||
val changelogText = stringResource(R.string.module_changelog)
|
val changelogText = stringResource(R.string.module_changelog)
|
||||||
val downloadingText = stringResource(R.string.module_downloading)
|
val downloadingText = stringResource(R.string.module_downloading)
|
||||||
@@ -316,13 +326,15 @@ private fun ModuleList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val refreshState = rememberPullRefreshState(refreshing = viewModel.isRefreshing,
|
val refreshState = rememberPullRefreshState(
|
||||||
onRefresh = { viewModel.fetchModuleList() })
|
refreshing = viewModel.isRefreshing,
|
||||||
Box(modifier.pullRefresh(refreshState)) {
|
onRefresh = { viewModel.fetchModuleList() }
|
||||||
val context = LocalContext.current
|
)
|
||||||
|
Box(
|
||||||
|
boxModifier.pullRefresh(refreshState)
|
||||||
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = modifier,
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
contentPadding = remember {
|
contentPadding = remember {
|
||||||
PaddingValues(
|
PaddingValues(
|
||||||
@@ -428,8 +440,11 @@ private fun ModuleList(
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar() {
|
private fun TopBar(
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
scrollBehavior = scrollBehavior,
|
||||||
title = { Text(stringResource(R.string.module)) },
|
title = { Text(stringResource(R.string.module)) },
|
||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||||
)
|
)
|
||||||
@@ -448,12 +463,32 @@ private fun ModuleItem(
|
|||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
|
val textDecoration = if (!module.remove) null else TextDecoration.LineThrough
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
val indication = LocalIndication.current
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable { onClick(module) }
|
.run {
|
||||||
|
if (module.hasWebUi) {
|
||||||
|
toggleable(
|
||||||
|
value = isChecked,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
role = Role.Button,
|
||||||
|
indication = indication,
|
||||||
|
onValueChange = { onClick(module) }
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
toggleable(
|
||||||
|
value = isChecked,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
role = Role.Switch,
|
||||||
|
indication = indication,
|
||||||
|
onValueChange = onCheckChanged,
|
||||||
|
enabled = !module.update
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
.padding(24.dp, 16.dp, 24.dp, 0.dp)
|
.padding(24.dp, 16.dp, 24.dp, 0.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -499,7 +534,8 @@ private fun ModuleItem(
|
|||||||
Switch(
|
Switch(
|
||||||
enabled = !module.update,
|
enabled = !module.update,
|
||||||
checked = isChecked,
|
checked = isChecked,
|
||||||
onCheckedChange = onCheckChanged
|
onCheckedChange = onCheckChanged,
|
||||||
|
interactionSource = if (!module.hasWebUi) interactionSource else null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -559,6 +595,7 @@ private fun ModuleItem(
|
|||||||
if (module.hasWebUi) {
|
if (module.hasWebUi) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = { onClick(module) },
|
onClick = { onClick(module) },
|
||||||
|
interactionSource = interactionSource
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ import androidx.compose.material3.ModalBottomSheet
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -47,6 +50,7 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.LineHeightStyle
|
import androidx.compose.ui.text.style.LineHeightStyle
|
||||||
@@ -91,11 +95,16 @@ import me.weishu.kernelsu.ui.util.shrinkModules
|
|||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingScreen(navigator: DestinationsNavigator) {
|
fun SettingScreen(navigator: DestinationsNavigator) {
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBar(onBack = {
|
TopBar(
|
||||||
navigator.popBackStack()
|
onBack = {
|
||||||
})
|
navigator.popBackStack()
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
|
)
|
||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
@@ -108,6 +117,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -459,15 +469,21 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(onBack: () -> Unit = {}) {
|
private fun TopBar(
|
||||||
|
onBack: () -> Unit = {},
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(stringResource(R.string.settings)) },
|
title = { Text(stringResource(R.string.settings)) },
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(
|
IconButton(
|
||||||
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)
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import androidx.compose.runtime.*
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
@@ -35,12 +36,13 @@ import me.weishu.kernelsu.R
|
|||||||
import me.weishu.kernelsu.ui.component.SearchAppBar
|
import me.weishu.kernelsu.ui.component.SearchAppBar
|
||||||
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
import me.weishu.kernelsu.ui.viewmodel.SuperUserViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@Composable
|
@Composable
|
||||||
fun SuperUserScreen(navigator: DestinationsNavigator) {
|
fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||||
val viewModel = viewModel<SuperUserViewModel>()
|
val viewModel = viewModel<SuperUserViewModel>()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (viewModel.appList.isEmpty()) {
|
if (viewModel.appList.isEmpty()) {
|
||||||
@@ -92,6 +94,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||||
@@ -105,7 +108,11 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
|||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.pullRefresh(refreshState)
|
.pullRefresh(refreshState)
|
||||||
) {
|
) {
|
||||||
LazyColumn(Modifier.fillMaxSize()) {
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
|
) {
|
||||||
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
|
items(viewModel.appList, key = { it.packageName + it.uid }) { app ->
|
||||||
AppItem(app) {
|
AppItem(app) {
|
||||||
navigator.navigate(AppProfileScreenDestination(app))
|
navigator.navigate(AppProfileScreenDestination(app))
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -44,6 +47,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalClipboardManager
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -66,7 +70,7 @@ import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel
|
|||||||
* @date 2023/10/20.
|
* @date 2023/10/20.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@Composable
|
@Composable
|
||||||
fun AppProfileTemplateScreen(
|
fun AppProfileTemplateScreen(
|
||||||
@@ -75,6 +79,7 @@ fun AppProfileTemplateScreen(
|
|||||||
) {
|
) {
|
||||||
val viewModel = viewModel<TemplateViewModel>()
|
val viewModel = viewModel<TemplateViewModel>()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (viewModel.templateList.isEmpty()) {
|
if (viewModel.templateList.isEmpty()) {
|
||||||
@@ -98,7 +103,8 @@ fun AppProfileTemplateScreen(
|
|||||||
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TopBar(onBack = { navigator.popBackStack() },
|
TopBar(
|
||||||
|
onBack = { navigator.popBackStack() },
|
||||||
onSync = {
|
onSync = {
|
||||||
scope.launch { viewModel.fetchTemplates(true) }
|
scope.launch { viewModel.fetchTemplates(true) }
|
||||||
},
|
},
|
||||||
@@ -129,7 +135,8 @@ fun AppProfileTemplateScreen(
|
|||||||
clipboardManager.setText(AnnotatedString(it))
|
clipboardManager.setText(AnnotatedString(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
@@ -157,9 +164,14 @@ fun AppProfileTemplateScreen(
|
|||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.pullRefresh(refreshState)
|
.pullRefresh(refreshState)
|
||||||
) {
|
) {
|
||||||
LazyColumn(Modifier.fillMaxSize(), contentPadding = remember {
|
LazyColumn(
|
||||||
PaddingValues(bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */)
|
modifier = Modifier
|
||||||
}) {
|
.fillMaxSize()
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
contentPadding = remember {
|
||||||
|
PaddingValues(bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */)
|
||||||
|
}
|
||||||
|
) {
|
||||||
items(viewModel.templateList, key = { it.id }) { app ->
|
items(viewModel.templateList, key = { it.id }) { app ->
|
||||||
TemplateItem(navigator, app)
|
TemplateItem(navigator, app)
|
||||||
}
|
}
|
||||||
@@ -215,7 +227,8 @@ private fun TopBar(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onSync: () -> Unit = {},
|
onSync: () -> Unit = {},
|
||||||
onImport: () -> Unit = {},
|
onImport: () -> Unit = {},
|
||||||
onExport: () -> Unit = {}
|
onExport: () -> Unit = {},
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
) {
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
@@ -261,6 +274,7 @@ private fun TopBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -26,6 +26,9 @@ import androidx.compose.material3.OutlinedTextField
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -34,6 +37,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.input.pointer.pointerInteropFilter
|
import androidx.compose.ui.input.pointer.pointerInteropFilter
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
@@ -56,7 +60,7 @@ import me.weishu.kernelsu.ui.viewmodel.toJSON
|
|||||||
* @author weishu
|
* @author weishu
|
||||||
* @date 2023/10/20.
|
* @date 2023/10/20.
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
|
||||||
@Destination<RootGraph>
|
@Destination<RootGraph>
|
||||||
@Composable
|
@Composable
|
||||||
fun TemplateEditorScreen(
|
fun TemplateEditorScreen(
|
||||||
@@ -72,6 +76,8 @@ fun TemplateEditorScreen(
|
|||||||
mutableStateOf(initialTemplate)
|
mutableStateOf(initialTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
BackHandler {
|
BackHandler {
|
||||||
navigator.navigateBack(result = !readOnly)
|
navigator.navigateBack(result = !readOnly)
|
||||||
}
|
}
|
||||||
@@ -111,13 +117,16 @@ fun TemplateEditorScreen(
|
|||||||
} else {
|
} else {
|
||||||
Toast.makeText(context, saveTemplateFailed, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, saveTemplateFailed, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
|
)
|
||||||
},
|
},
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
|
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.pointerInteropFilter {
|
.pointerInteropFilter {
|
||||||
// disable click and ripple if readOnly
|
// disable click and ripple if readOnly
|
||||||
@@ -246,7 +255,8 @@ private fun TopBar(
|
|||||||
summary: String = "",
|
summary: String = "",
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onDelete: () -> Unit = {},
|
onDelete: () -> Unit = {},
|
||||||
onSave: () -> Unit = {}
|
onSave: () -> Unit = {},
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
) {
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
@@ -280,7 +290,8 @@ private fun TopBar(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
|
||||||
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,8 +316,6 @@ private fun TextEdit(
|
|||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.error
|
color = MaterialTheme.colorScheme.error
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isError = isError,
|
isError = isError,
|
||||||
@@ -322,7 +331,7 @@ private fun TextEdit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun isValidTemplateId(id: String): Boolean {
|
private fun isValidTemplateId(id: String): Boolean {
|
||||||
return Regex("""^([A-Za-z]{1}[A-Za-z\d_]*\.)*[A-Za-z][A-Za-z\d_]*$""").matches(id)
|
return Regex("""^([A-Za-z][A-Za-z\d_]*\.)*[A-Za-z][A-Za-z\d_]*$""").matches(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isTemplateExist(id: String): Boolean {
|
private fun isTemplateExist(id: String): Boolean {
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
|
|
||||||
<group
|
|
||||||
android:scaleX="0.135"
|
|
||||||
android:scaleY="0.135">
|
|
||||||
<path
|
|
||||||
android:fillColor="#ffffff"
|
|
||||||
android:pathData="M 0 0 H 800 V 800 H 0 V 0 Z" />
|
|
||||||
</group>
|
|
||||||
</vector>
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@color/ic_launcher_background" />
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
1
manager/app/src/main/res/resources.properties
Normal file
1
manager/app/src/main/res/resources.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
unqualifiedResLocale=en-US
|
||||||
4
manager/app/src/main/res/values/colors.xml
Normal file
4
manager/app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#FFFFFFFF</color>
|
||||||
|
</resources>
|
||||||
@@ -79,6 +79,9 @@ subprojects {
|
|||||||
versionCode = managerVersionCode
|
versionCode = managerVersionCode
|
||||||
versionName = managerVersionName
|
versionName = managerVersionName
|
||||||
}
|
}
|
||||||
|
ndk {
|
||||||
|
abiFilters += listOf("arm64-v8a", "x86_64", "riscv64")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lint {
|
lint {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ kotlin = "2.0.20"
|
|||||||
ksp = "2.0.20-1.0.25"
|
ksp = "2.0.20-1.0.25"
|
||||||
compose-bom = "2024.09.02"
|
compose-bom = "2024.09.02"
|
||||||
lifecycle = "2.8.6"
|
lifecycle = "2.8.6"
|
||||||
accompanist = "0.36.0"
|
|
||||||
navigation = "2.8.1"
|
navigation = "2.8.1"
|
||||||
activity-compose = "1.9.2"
|
activity-compose = "1.9.2"
|
||||||
kotlinx-coroutines = "1.9.0"
|
kotlinx-coroutines = "1.9.0"
|
||||||
@@ -51,10 +50,6 @@ androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "l
|
|||||||
|
|
||||||
androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
|
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-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" }
|
||||||
com-github-topjohnwu-libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" }
|
com-github-topjohnwu-libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" }
|
||||||
com-github-topjohnwu-libsu-io= { group = "com.github.topjohnwu.libsu", name = "io", version.ref = "libsu" }
|
com-github-topjohnwu-libsu-io= { group = "com.github.topjohnwu.libsu", name = "io", version.ref = "libsu" }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
Reference in New Issue
Block a user