diff --git a/manager/.gitignore b/manager/.gitignore index dd42da43..a595ddf7 100644 --- a/manager/.gitignore +++ b/manager/.gitignore @@ -1,9 +1,10 @@ *.iml .gradle -local.properties .idea +.kotlin .DS_Store build captures .cxx +local.properties key.jks diff --git a/manager/app/build.gradle.kts b/manager/app/build.gradle.kts index 7d3792f4..1f98e4a1 100644 --- a/manager/app/build.gradle.kts +++ b/manager/app/build.gradle.kts @@ -1,4 +1,7 @@ +@file:Suppress("UnstableApiUsage") + import com.android.build.gradle.internal.api.BaseVariantOutputImpl +import com.android.build.gradle.tasks.PackageAndroidArtifact plugins { alias(libs.plugins.agp.app) @@ -46,7 +49,13 @@ android { useLegacyPackaging = true } 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 { + doFirst { appMetadata.asFile.orNull?.writeText("") } + } + + dependenciesInfo { + includeInApk = false + includeInBundle = false + } + + androidResources { + generateLocaleConfig = true + } } dependencies { @@ -87,10 +110,6 @@ dependencies { implementation(libs.androidx.lifecycle.runtime.ktx) 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) ksp(libs.compose.destinations.ksp) diff --git a/manager/app/proguard-rules.pro b/manager/app/proguard-rules.pro index dcaf39ce..e69de29b 100644 --- a/manager/app/proguard-rules.pro +++ b/manager/app/proguard-rules.pro @@ -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 diff --git a/manager/app/src/main/AndroidManifest.xml b/manager/app/src/main/AndroidManifest.xml index 618dd974..11cda5f2 100644 --- a/manager/app/src/main/AndroidManifest.xml +++ b/manager/app/src/main/AndroidManifest.xml @@ -12,8 +12,8 @@ android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:supportsRtl="true" android:networkSecurityConfig="@xml/network_security_config" + android:supportsRtl="true" android:theme="@style/Theme.KernelSU" tools:targetApi="34"> - - - if (!fullFeatured && destination.rootRequired) return@forEach diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt index 0807d052..80344739 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/AboutCard.kt @@ -1,7 +1,5 @@ package me.weishu.kernelsu.ui.component -import android.text.method.LinkMovementMethod -import android.widget.TextView import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column 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.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.draw.scale +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource 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.unit.dp import androidx.compose.ui.unit.sp -import androidx.compose.ui.viewinterop.AndroidView 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.R @@ -36,9 +38,8 @@ import me.weishu.kernelsu.R @Composable fun AboutCard() { ElevatedCard( - modifier = Modifier - .fillMaxWidth(), - shape = RoundedCornerShape(8.dp), + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(8.dp) ) { Row( modifier = Modifier @@ -52,7 +53,9 @@ fun AboutCard() { @Composable fun AboutDialog(dismiss: () -> Unit) { - Dialog(onDismissRequest = { dismiss() }) { + Dialog( + onDismissRequest = { dismiss() } + ) { AboutCard() } } @@ -60,21 +63,20 @@ fun AboutDialog(dismiss: () -> Unit) { @Composable private fun AboutCardContent() { Column( - modifier = Modifier - .fillMaxWidth() + modifier = Modifier.fillMaxWidth() ) { - val drawable = ResourcesCompat.getDrawable( - LocalContext.current.resources, - R.mipmap.ic_launcher, - LocalContext.current.theme - ) - Row { - Image( - painter = rememberDrawablePainter(drawable), - contentDescription = "icon", - modifier = Modifier.size(40.dp) - ) + Surface( + modifier = Modifier.size(40.dp), + color = colorResource(id = R.color.ic_launcher_background), + shape = CircleShape + ) { + Image( + painter = painterResource(id = R.drawable.ic_launcher_foreground), + contentDescription = "icon", + modifier = Modifier.scale(1.4f) + ) + } Spacer(modifier = Modifier.width(12.dp)) @@ -93,31 +95,31 @@ private fun AboutCardContent() { Spacer(modifier = Modifier.height(8.dp)) - HtmlText( - html = stringResource( + val annotatedString = AnnotatedString.Companion.fromHtml( + htmlString = stringResource( id = R.string.about_source_code, "GitHub", "Telegram" + ), + 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()) - } - ) } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt index b6f7dbe7..b388cb49 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SearchBar.kt @@ -23,6 +23,7 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -52,6 +53,7 @@ fun SearchAppBar( onBackClick: (() -> Unit)? = null, onConfirm: (() -> Unit)? = null, dropdownContent: @Composable (() -> Unit)? = null, + scrollBehavior: TopAppBarScrollBehavior? = null ) { val keyboardController = LocalSoftwareKeyboardController.current 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 @Composable private fun SearchAppBarPreview() { diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt index 6db20b16..e537175f 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/SettingsItem.kt @@ -1,14 +1,18 @@ 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.ListItem import androidx.compose.material3.RadioButton import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.semantics.Role @Composable fun SwitchItem( @@ -19,10 +23,18 @@ fun SwitchItem( enabled: Boolean = true, onCheckedChange: (Boolean) -> Unit ) { + val interactionSource = remember { MutableInteractionSource() } + ListItem( - modifier = Modifier.clickable { - onCheckedChange.invoke(!checked) - }, + modifier = Modifier + .toggleable( + value = checked, + interactionSource = interactionSource, + role = Role.Switch, + enabled = enabled, + indication = LocalIndication.current, + onValueChange = onCheckedChange + ), headlineContent = { Text(title) }, @@ -30,7 +42,12 @@ fun SwitchItem( { Icon(icon, title) } }, trailingContent = { - Switch(checked = checked, enabled = enabled, onCheckedChange = onCheckedChange) + Switch( + checked = checked, + enabled = enabled, + onCheckedChange = onCheckedChange, + interactionSource = interactionSource + ) }, supportingContent = { if (summary != null) { @@ -52,6 +69,6 @@ fun RadioItem( }, leadingContent = { RadioButton(selected = selected, onClick = onClick) - }, + } ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt index f7035a67..502e935e 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt @@ -34,6 +34,8 @@ import androidx.compose.material3.ListItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -43,6 +45,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity @@ -78,6 +81,7 @@ import me.weishu.kernelsu.ui.viewmodel.getTemplateInfoById * @author weishu * @date 2023/5/16. */ +@OptIn(ExperimentalMaterial3Api::class) @Destination @Composable fun AppProfileScreen( @@ -86,6 +90,7 @@ fun AppProfileScreen( ) { val context = LocalContext.current val snackbarHost = LocalSnackbarHost.current + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() val scope = rememberCoroutineScope() val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label) val failToUpdateSepolicy = stringResource(R.string.failed_to_update_sepolicy).format(appInfo.label) @@ -100,19 +105,24 @@ fun AppProfileScreen( } Scaffold( - topBar = { TopBar { navigator.popBackStack() } }, + topBar = { + TopBar( + onBack = { navigator.popBackStack() }, + scrollBehavior = scrollBehavior + ) + }, contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { paddingValues -> AppProfileInner( modifier = Modifier .padding(paddingValues) + .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(rememberScrollState()), packageName = appInfo.packageName, appLabel = appInfo.label, appIcon = { AsyncImage( - model = ImageRequest.Builder(context).data(appInfo.packageInfo).crossfade(true) - .build(), + model = ImageRequest.Builder(context).data(appInfo.packageInfo).crossfade(true).build(), contentDescription = appInfo.label, modifier = Modifier .padding(4.dp) @@ -242,7 +252,10 @@ private enum class Mode(@StringRes private val res: Int) { @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(onBack: () -> Unit) { +private fun TopBar( + onBack: () -> Unit, + scrollBehavior: TopAppBarScrollBehavior? = null +) { TopAppBar( title = { Text(stringResource(R.string.profile)) @@ -252,7 +265,8 @@ private fun TopBar(onBack: () -> Unit) { onClick = onBack ) { 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 ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt index 3afdfb85..af89b03c 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt @@ -24,6 +24,9 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -34,6 +37,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier 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.text.font.FontFamily import androidx.compose.ui.tooling.preview.Preview @@ -70,6 +74,7 @@ enum class FlashingStatus { * @author weishu * @date 2023/1/1. */ +@OptIn(ExperimentalMaterial3Api::class) @Composable @Destination fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { @@ -81,6 +86,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { val snackBarHost = LocalSnackbarHost.current val scope = rememberCoroutineScope() val scrollState = rememberScrollState() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) var flashing by rememberSaveable { mutableStateOf(FlashingStatus.FLASHING) } @@ -126,7 +132,8 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { file.writeText(logContent.toString()) snackBarHost.showSnackbar("Log saved to ${file.absolutePath}") } - } + }, + scrollBehavior = scrollBehavior ) }, floatingActionButton = { @@ -154,6 +161,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { modifier = Modifier .fillMaxSize(1f) .padding(innerPadding) + .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(scrollState), ) { LaunchedEffect(text) { @@ -207,7 +215,12 @@ fun flashIt( @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) { +private fun TopBar( + status: FlashingStatus, + onBack: () -> Unit = {}, + onSave: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null +) { TopAppBar( title = { 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 ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt index 7fcbb2df..0fa3b34a 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Home.kt @@ -22,6 +22,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler 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.module.LatestVersionInfo +@OptIn(ExperimentalMaterial3Api::class) @Destination(start = true) @Composable fun HomeScreen(navigator: DestinationsNavigator) { val kernelVersion = getKernelVersion() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) Scaffold( topBar = { - TopBar(kernelVersion, onSettingsClick = { - navigator.navigate(SettingScreenDestination) - }, onInstallClick = { - navigator.navigate(InstallScreenDestination) - }) + TopBar( + kernelVersion, + onSettingsClick = { + navigator.navigate(SettingScreenDestination) + }, + onInstallClick = { + navigator.navigate(InstallScreenDestination) + }, + scrollBehavior = scrollBehavior + ) }, contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { innerPadding -> Column( modifier = Modifier .padding(innerPadding) - .padding(horizontal = 16.dp) - .verticalScroll(rememberScrollState()), + .nestedScroll(scrollBehavior.nestedScrollConnection) + .verticalScroll(rememberScrollState()) + .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { val isManager = Natives.becomeManager(ksuApp.packageName) @@ -158,7 +167,8 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") { private fun TopBar( kernelVersion: KernelVersion, onInstallClick: () -> Unit, - onSettingsClick: () -> Unit + onSettingsClick: () -> Unit, + scrollBehavior: TopAppBarScrollBehavior? = null ) { TopAppBar( title = { Text(stringResource(R.string.app_name)) }, @@ -187,8 +197,8 @@ private fun TopBar( RebootDropdownItem(id = R.string.reboot) - val pm = - LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager? + val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager? + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) { 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 ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt index b0f5d7ac..01f7ce30 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt @@ -6,7 +6,8 @@ import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts 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.Row 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.padding 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.automirrored.filled.ArrowBack 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.Text 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.getValue import androidx.compose.runtime.mutableStateOf @@ -35,7 +42,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.maxkeppeker.sheets.core.models.base.Header @@ -63,6 +72,7 @@ import me.weishu.kernelsu.ui.util.rootAvailable * @author weishu * @date 2024/3/12. */ +@OptIn(ExperimentalMaterial3Api::class) @Destination @Composable fun InstallScreen(navigator: DestinationsNavigator) { @@ -118,15 +128,24 @@ fun InstallScreen(navigator: DestinationsNavigator) { }) } + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + Scaffold( topBar = { TopBar( - onBack = { navigator.popBackStack() }, onLkmUpload = onLkmUpload + onBack = { navigator.popBackStack() }, + onLkmUpload = onLkmUpload, + scrollBehavior = scrollBehavior ) }, contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) - ) { - Column(modifier = Modifier.padding(it)) { + ) { innerPadding -> + Column( + modifier = Modifier + .padding(innerPadding) + .nestedScroll(scrollBehavior.nestedScrollConnection) + .verticalScroll(rememberScrollState()) + ) { SelectInstallMethod { method -> installMethod = method } @@ -239,16 +258,31 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) { Column { radioOptions.forEach { option -> - Row(verticalAlignment = Alignment.CenterVertically, + val interactionSource = remember { MutableInteractionSource() } + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier .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) - }) { - RadioButton(selected = option.javaClass == selectedOption?.javaClass, onClick = { - onClick(option) - }) - Column { + }, + interactionSource = interactionSource + ) + Column( + modifier = Modifier.padding(vertical = 12.dp) + ) { Text( text = stringResource(id = option.label), fontSize = MaterialTheme.typography.titleMedium.fontSize, @@ -300,7 +334,11 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle { @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) { +private fun TopBar( + onBack: () -> Unit = {}, + onLkmUpload: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null +) { TopAppBar( title = { Text(stringResource(R.string.install)) }, navigationIcon = { IconButton( @@ -311,7 +349,8 @@ private fun TopBar(onBack: () -> Unit = {}, onLkmUpload: () -> Unit = {}) { Icon(Icons.Filled.FileUpload, contentDescription = null) } }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) + windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + scrollBehavior = scrollBehavior ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt index c224dfa3..11b6d367 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt @@ -7,7 +7,8 @@ import android.util.Log import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult 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.Box 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.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.selection.toggleable import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.icons.Icons @@ -45,6 +47,9 @@ import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -56,8 +61,10 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign 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 okhttp3.OkHttpClient +@OptIn(ExperimentalMaterial3Api::class) @Destination @Composable fun ModuleScreen(navigator: DestinationsNavigator) { @@ -106,9 +114,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) { val hideInstallButton = isSafeMode || hasMagisk + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + Scaffold( topBar = { - TopBar() + TopBar(scrollBehavior = scrollBehavior) }, floatingActionButton = { if (hideInstallButton) { @@ -146,7 +156,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) { }, contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { innerPadding -> - when { hasMagisk -> { Box( @@ -161,16 +170,15 @@ fun ModuleScreen(navigator: DestinationsNavigator) { ) } } - else -> { ModuleList( - viewModel = viewModel, modifier = Modifier - .padding(innerPadding) - .fillMaxSize(), - onInstallModule = - { + viewModel = viewModel, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + boxModifier = Modifier.padding(innerPadding), + onInstallModule = { navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it))) - }, onClickModule = { id, name, hasWebUi -> + }, + onClickModule = { id, name, hasWebUi -> if (hasWebUi) { context.startActivity( Intent(context, WebUIActivity::class.java) @@ -179,7 +187,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) { .putExtra("name", name) ) } - }) + } + ) } } } @@ -190,6 +199,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) { private fun ModuleList( viewModel: ModuleViewModel, modifier: Modifier = Modifier, + boxModifier: Modifier = Modifier, onInstallModule: (Uri) -> 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 failedUninstall = stringResource(R.string.module_uninstall_failed) val successUninstall = stringResource(R.string.module_uninstall_success) - val reboot = stringResource(id = R.string.reboot) - val rebootToApply = stringResource(id = R.string.reboot_to_apply) - val moduleStr = stringResource(id = R.string.module) - val uninstall = stringResource(id = R.string.uninstall) - val cancel = stringResource(id = android.R.string.cancel) - val moduleUninstallConfirm = stringResource(id = R.string.module_uninstall_confirm) + val reboot = stringResource(R.string.reboot) + val rebootToApply = stringResource(R.string.reboot_to_apply) + val moduleStr = stringResource(R.string.module) + val uninstall = stringResource(R.string.uninstall) + val cancel = stringResource(android.R.string.cancel) + val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm) val updateText = stringResource(R.string.module_update) val changelogText = stringResource(R.string.module_changelog) val downloadingText = stringResource(R.string.module_downloading) @@ -316,13 +326,15 @@ private fun ModuleList( } } - val refreshState = rememberPullRefreshState(refreshing = viewModel.isRefreshing, - onRefresh = { viewModel.fetchModuleList() }) - Box(modifier.pullRefresh(refreshState)) { - val context = LocalContext.current - + val refreshState = rememberPullRefreshState( + refreshing = viewModel.isRefreshing, + onRefresh = { viewModel.fetchModuleList() } + ) + Box( + boxModifier.pullRefresh(refreshState) + ) { LazyColumn( - modifier = Modifier.fillMaxSize(), + modifier = modifier, verticalArrangement = Arrangement.spacedBy(16.dp), contentPadding = remember { PaddingValues( @@ -428,8 +440,11 @@ private fun ModuleList( @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar() { +private fun TopBar( + scrollBehavior: TopAppBarScrollBehavior? = null +) { TopAppBar( + scrollBehavior = scrollBehavior, title = { Text(stringResource(R.string.module)) }, windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) @@ -448,12 +463,32 @@ private fun ModuleItem( ElevatedCard( modifier = Modifier.fillMaxWidth() ) { - val textDecoration = if (!module.remove) null else TextDecoration.LineThrough + val interactionSource = remember { MutableInteractionSource() } + val indication = LocalIndication.current Column( 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) ) { Row( @@ -499,7 +534,8 @@ private fun ModuleItem( Switch( enabled = !module.update, checked = isChecked, - onCheckedChange = onCheckChanged + onCheckedChange = onCheckChanged, + interactionSource = if (!module.hasWebUi) interactionSource else null ) } } @@ -559,6 +595,7 @@ private fun ModuleItem( if (module.hasWebUi) { TextButton( onClick = { onClick(module) }, + interactionSource = interactionSource ) { Text( fontFamily = MaterialTheme.typography.labelMedium.fontFamily, diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt index a0f32d80..42caaa85 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Settings.kt @@ -37,6 +37,9 @@ import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Text 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.getValue import androidx.compose.runtime.mutableStateOf @@ -47,6 +50,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.LineHeightStyle @@ -91,11 +95,16 @@ import me.weishu.kernelsu.ui.util.shrinkModules @Destination @Composable fun SettingScreen(navigator: DestinationsNavigator) { + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + Scaffold( topBar = { - TopBar(onBack = { - navigator.popBackStack() - }) + TopBar( + onBack = { + navigator.popBackStack() + }, + scrollBehavior = scrollBehavior + ) }, contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { paddingValues -> @@ -108,6 +117,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { Column( modifier = Modifier .padding(paddingValues) + .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(rememberScrollState()) ) { @@ -459,15 +469,21 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle { @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun TopBar(onBack: () -> Unit = {}) { +private fun TopBar( + onBack: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null +) { TopAppBar( title = { Text(stringResource(R.string.settings)) }, navigationIcon = { IconButton( 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 ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt index e41345b0..14f2aad1 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt @@ -17,6 +17,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.viewmodel.SuperUserViewModel -@OptIn(ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Destination @Composable fun SuperUserScreen(navigator: DestinationsNavigator) { val viewModel = viewModel() val scope = rememberCoroutineScope() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) LaunchedEffect(Unit) { if (viewModel.appList.isEmpty()) { @@ -92,6 +94,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { } } }, + scrollBehavior = scrollBehavior ) }, contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) @@ -105,7 +108,11 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { .padding(innerPadding) .pullRefresh(refreshState) ) { - LazyColumn(Modifier.fillMaxSize()) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection) + ) { items(viewModel.appList, key = { it.packageName + it.uid }) { app -> AppItem(app) { navigator.navigate(AppProfileScreenDestination(app)) diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt index 1ba58059..33bbcd6a 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Template.kt @@ -35,6 +35,9 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -44,6 +47,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -66,7 +70,7 @@ import me.weishu.kernelsu.ui.viewmodel.TemplateViewModel * @date 2023/10/20. */ -@OptIn(ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Destination @Composable fun AppProfileTemplateScreen( @@ -75,6 +79,7 @@ fun AppProfileTemplateScreen( ) { val viewModel = viewModel() val scope = rememberCoroutineScope() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) LaunchedEffect(Unit) { if (viewModel.templateList.isEmpty()) { @@ -98,7 +103,8 @@ fun AppProfileTemplateScreen( Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() } } - TopBar(onBack = { navigator.popBackStack() }, + TopBar( + onBack = { navigator.popBackStack() }, onSync = { scope.launch { viewModel.fetchTemplates(true) } }, @@ -129,7 +135,8 @@ fun AppProfileTemplateScreen( clipboardManager.setText(AnnotatedString(it)) } } - } + }, + scrollBehavior = scrollBehavior ) }, floatingActionButton = { @@ -157,9 +164,14 @@ fun AppProfileTemplateScreen( .padding(innerPadding) .pullRefresh(refreshState) ) { - LazyColumn(Modifier.fillMaxSize(), contentPadding = remember { - PaddingValues(bottom = 16.dp + 16.dp + 56.dp /* Scaffold Fab Spacing + Fab container height */) - }) { + LazyColumn( + 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 -> TemplateItem(navigator, app) } @@ -215,7 +227,8 @@ private fun TopBar( onBack: () -> Unit, onSync: () -> Unit = {}, onImport: () -> Unit = {}, - onExport: () -> Unit = {} + onExport: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null ) { TopAppBar( 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 ) } \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt index 53f5beff..030320ba 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/TemplateEditor.kt @@ -26,6 +26,9 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -34,6 +37,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -56,7 +60,7 @@ import me.weishu.kernelsu.ui.viewmodel.toJSON * @author weishu * @date 2023/10/20. */ -@OptIn(ExperimentalComposeUiApi::class) +@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) @Destination @Composable fun TemplateEditorScreen( @@ -72,6 +76,8 @@ fun TemplateEditorScreen( mutableStateOf(initialTemplate) } + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) + BackHandler { navigator.navigateBack(result = !readOnly) } @@ -111,13 +117,16 @@ fun TemplateEditorScreen( } else { Toast.makeText(context, saveTemplateFailed, Toast.LENGTH_SHORT).show() } - }) + }, + scrollBehavior = scrollBehavior + ) }, contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) { innerPadding -> Column( modifier = Modifier .padding(innerPadding) + .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(rememberScrollState()) .pointerInteropFilter { // disable click and ripple if readOnly @@ -246,7 +255,8 @@ private fun TopBar( summary: String = "", onBack: () -> Unit, onDelete: () -> Unit = {}, - onSave: () -> Unit = {} + onSave: () -> Unit = {}, + scrollBehavior: TopAppBarScrollBehavior? = null ) { TopAppBar( 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, color = MaterialTheme.colorScheme.error ) - } else { - null } }, isError = isError, @@ -322,7 +331,7 @@ private fun TextEdit( } 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 { diff --git a/manager/app/src/main/res/drawable/ic_launcher_background.xml b/manager/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 900e2d8c..00000000 --- a/manager/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml index b070c763..f30783b2 100644 --- a/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ b/manager/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/manager/app/src/main/res/resources.properties b/manager/app/src/main/res/resources.properties new file mode 100644 index 00000000..d5a3ddc9 --- /dev/null +++ b/manager/app/src/main/res/resources.properties @@ -0,0 +1 @@ +unqualifiedResLocale=en-US \ No newline at end of file diff --git a/manager/app/src/main/res/values/colors.xml b/manager/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..a5b623aa --- /dev/null +++ b/manager/app/src/main/res/values/colors.xml @@ -0,0 +1,4 @@ + + + #FFFFFFFF + \ No newline at end of file diff --git a/manager/build.gradle.kts b/manager/build.gradle.kts index acfe9d43..3bb25ee9 100644 --- a/manager/build.gradle.kts +++ b/manager/build.gradle.kts @@ -79,6 +79,9 @@ subprojects { versionCode = managerVersionCode versionName = managerVersionName } + ndk { + abiFilters += listOf("arm64-v8a", "x86_64", "riscv64") + } } lint { diff --git a/manager/gradle/libs.versions.toml b/manager/gradle/libs.versions.toml index 30c2bf85..7492e81c 100644 --- a/manager/gradle/libs.versions.toml +++ b/manager/gradle/libs.versions.toml @@ -4,7 +4,6 @@ kotlin = "2.0.20" ksp = "2.0.20-1.0.25" compose-bom = "2024.09.02" lifecycle = "2.8.6" -accompanist = "0.36.0" navigation = "2.8.1" activity-compose = "1.9.2" kotlinx-coroutines = "1.9.0" @@ -51,10 +50,6 @@ androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "l 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-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" } diff --git a/manager/gradle/wrapper/gradle-wrapper.properties b/manager/gradle/wrapper/gradle-wrapper.properties index 09523c0e..df97d72b 100644 --- a/manager/gradle/wrapper/gradle-wrapper.properties +++ b/manager/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME 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 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME