diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/component/ImageEditorDialog.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/component/ImageEditorDialog.kt new file mode 100644 index 00000000..0bb280cc --- /dev/null +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/component/ImageEditorDialog.kt @@ -0,0 +1,140 @@ +package com.sukisu.ultra.ui.component + +import android.net.Uri +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectTransformGestures +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import coil.compose.AsyncImage +import coil.request.ImageRequest +import com.sukisu.ultra.R +import com.sukisu.ultra.ui.util.BackgroundTransformation +import com.sukisu.ultra.ui.util.saveTransformedBackground + +@Composable +fun ImageEditorDialog( + imageUri: Uri, + onDismiss: () -> Unit, + onConfirm: (Uri) -> Unit +) { + var scale by remember { mutableFloatStateOf(1f) } + var offsetX by remember { mutableFloatStateOf(0f) } + var offsetY by remember { mutableFloatStateOf(0f) } + val context = LocalContext.current + + Dialog( + onDismissRequest = onDismiss, + properties = DialogProperties( + dismissOnBackPress = true, + dismissOnClickOutside = false, + usePlatformDefaultWidth = false + ) + ) { + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.9f)) + ) { + // 主图片区域 + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(imageUri) + .crossfade(true) + .build(), + contentDescription = stringResource(R.string.settings_custom_background), + contentScale = ContentScale.Fit, + modifier = Modifier + .fillMaxSize() + .graphicsLayer( + scaleX = scale, + scaleY = scale, + translationX = offsetX, + translationY = offsetY + ) + .pointerInput(Unit) { + detectTransformGestures { _, pan, zoom, _ -> + scale = (scale * zoom).coerceIn(0.5f, 3f) + + // 限制平移范围,防止图片完全移出屏幕 + val maxOffset = size.width * (scale - 1) / 2 + offsetX = (offsetX + pan.x).coerceIn(-maxOffset, maxOffset) + offsetY = (offsetY + pan.y).coerceIn(-maxOffset, maxOffset) + } + } + ) + + // 顶部工具栏 + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + .align(Alignment.TopCenter), + horizontalArrangement = Arrangement.SpaceBetween + ) { + IconButton( + onClick = onDismiss, + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(Color.Black.copy(alpha = 0.6f)) + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = stringResource(R.string.cancel), + tint = Color.White + ) + } + + IconButton( + onClick = { + val transformation = BackgroundTransformation(scale, offsetX, offsetY) + val savedUri = context.saveTransformedBackground(imageUri, transformation) + savedUri?.let { onConfirm(it) } + }, + modifier = Modifier + .clip(RoundedCornerShape(8.dp)) + .background(Color.Black.copy(alpha = 0.6f)) + ) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = stringResource(R.string.confirm), + tint = Color.White + ) + } + } + + // 底部提示 + Box( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + .clip(RoundedCornerShape(8.dp)) + .background(Color.Black.copy(alpha = 0.6f)) + .padding(16.dp) + .align(Alignment.BottomCenter) + ) { + Text( + text = stringResource(id = R.string.image_editor_hint), + color = Color.White, + style = MaterialTheme.typography.bodyMedium + ) + } + } + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt index c7f00c30..7cb59966 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt @@ -35,6 +35,7 @@ import com.topjohnwu.superuser.Shell import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import com.sukisu.ultra.ui.component.ImageEditorDialog import com.sukisu.ultra.ui.component.SwitchItem import com.sukisu.ultra.ui.theme.* import com.sukisu.ultra.ui.util.* @@ -127,7 +128,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { prefs.edit { putBoolean("is_hide_susfs_status", newValue) } isHideSusfsStatus = newValue } - + // SELinux 状态 var selinuxEnabled by remember { mutableStateOf(Shell.cmd("getenforce").exec().out.firstOrNull() == "Enforcing") @@ -140,6 +141,10 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { mutableStateOf(ThemeConfig.customBackgroundUri != null) } + // 图片编辑状态 + var showImageEditor by remember { mutableStateOf(false) } + var selectedImageUri by remember { mutableStateOf(null) } + // 初始化卡片配置 val systemIsDark = isSystemInDarkTheme() LaunchedEffect(Unit) { @@ -183,13 +188,31 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { ActivityResultContracts.GetContent() ) { uri: Uri? -> uri?.let { - context.saveCustomBackground(it) - isCustomBackgroundEnabled = true - CardConfig.cardElevation = 0.dp - saveCardConfig(context) + selectedImageUri = it + showImageEditor = true } } + // 显示图片编辑对话框 + if (showImageEditor && selectedImageUri != null) { + ImageEditorDialog( + imageUri = selectedImageUri!!, + onDismiss = { + showImageEditor = false + selectedImageUri = null + }, + onConfirm = { transformedUri -> + context.saveAndApplyCustomBackground(transformedUri) + isCustomBackgroundEnabled = true + CardConfig.cardElevation = 0.dp + CardConfig.isCustomBackgroundEnabled = true + saveCardConfig(context) + showImageEditor = false + selectedImageUri = null + } + ) + } + Scaffold( topBar = { TopAppBar( @@ -439,6 +462,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { CardConfig.cardElevation = CardConfig.defaultElevation CardConfig.cardAlpha = 0.45f CardConfig.isCustomAlphaSet = false + CardConfig.isCustomBackgroundEnabled = false saveCardConfig(context) cardAlpha = 0.35f themeMode = 0 @@ -451,45 +475,45 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { ) } ) - // 透明度 Slider - AnimatedVisibility( - visible = ThemeConfig.customBackgroundUri != null && showCardSettings, - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) - ) { - ListItem( - leadingContent = { Icon(Icons.Filled.Opacity, null) }, - headlineContent = { Text(stringResource(R.string.settings_card_alpha)) }, - supportingContent = { - Slider( - value = cardAlpha, - onValueChange = { newValue -> - cardAlpha = newValue - CardConfig.cardAlpha = newValue - CardConfig.isCustomAlphaSet = true - prefs.edit { putBoolean("is_custom_alpha_set", true) } - prefs.edit { putFloat("card_alpha", newValue) } - }, - onValueChangeFinished = { - CoroutineScope(Dispatchers.IO).launch { - saveCardConfig(context) - } - }, - valueRange = 0f..1f, - colors = getSliderColors(cardAlpha, useCustomColors = true), - thumb = { - SliderDefaults.Thumb( - interactionSource = remember { MutableInteractionSource() }, - thumbSize = DpSize(0.dp, 0.dp) - ) + // 透明度 Slider + AnimatedVisibility( + visible = ThemeConfig.customBackgroundUri != null && showCardSettings, + modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) + ) { + ListItem( + leadingContent = { Icon(Icons.Filled.Opacity, null) }, + headlineContent = { Text(stringResource(R.string.settings_card_alpha)) }, + supportingContent = { + Slider( + value = cardAlpha, + onValueChange = { newValue -> + cardAlpha = newValue + CardConfig.cardAlpha = newValue + CardConfig.isCustomAlphaSet = true + prefs.edit { putBoolean("is_custom_alpha_set", true) } + prefs.edit { putFloat("card_alpha", newValue) } + }, + onValueChangeFinished = { + CoroutineScope(Dispatchers.IO).launch { + saveCardConfig(context) } - ) - } - ) - } - AnimatedVisibility( - visible = ThemeConfig.customBackgroundUri != null && showCardSettings, - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) - ){ + }, + valueRange = 0f..1f, + colors = getSliderColors(cardAlpha, useCustomColors = true), + thumb = { + SliderDefaults.Thumb( + interactionSource = remember { MutableInteractionSource() }, + thumbSize = DpSize(0.dp, 0.dp) + ) + } + ) + } + ) + } + AnimatedVisibility( + visible = ThemeConfig.customBackgroundUri != null && showCardSettings, + modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) + ){ ListItem( leadingContent = { Icon(Icons.Filled.DarkMode, null) }, headlineContent = { Text(stringResource(R.string.theme_mode)) }, @@ -498,69 +522,69 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { showThemeModeDialog = true } ) - } + } - // 主题模式选择对话框 - if (showThemeModeDialog) { - AlertDialog( - onDismissRequest = { showThemeModeDialog = false }, - title = { Text(stringResource(R.string.theme_mode)) }, - text = { - Column { - themeOptions.forEachIndexed { index, option -> - Row( - modifier = Modifier - .fillMaxWidth() - .clickable { - themeMode = index - val newThemeMode = when(index) { - 0 -> null // 跟随系统 - 1 -> false // 浅色 - 2 -> true // 深色 - else -> null - } - context.saveThemeMode(newThemeMode) - when (index) { - 2 -> { - ThemeConfig.forceDarkMode = true - CardConfig.isUserLightModeEnabled = false - CardConfig.isUserDarkModeEnabled = true - CardConfig.save(context) - } - 1 -> { - ThemeConfig.forceDarkMode = false - CardConfig.isUserLightModeEnabled = true - CardConfig.isUserDarkModeEnabled = false - CardConfig.save(context) - } - 0 -> { - ThemeConfig.forceDarkMode = null - CardConfig.isUserLightModeEnabled = false - CardConfig.isUserDarkModeEnabled = false - CardConfig.save(context) - } - } - showThemeModeDialog = false + // 主题模式选择对话框 + if (showThemeModeDialog) { + AlertDialog( + onDismissRequest = { showThemeModeDialog = false }, + title = { Text(stringResource(R.string.theme_mode)) }, + text = { + Column { + themeOptions.forEachIndexed { index, option -> + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + themeMode = index + val newThemeMode = when(index) { + 0 -> null // 跟随系统 + 1 -> false // 浅色 + 2 -> true // 深色 + else -> null + } + context.saveThemeMode(newThemeMode) + when (index) { + 2 -> { + ThemeConfig.forceDarkMode = true + CardConfig.isUserLightModeEnabled = false + CardConfig.isUserDarkModeEnabled = true + CardConfig.save(context) } - .padding(vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically - ) { - RadioButton( - selected = themeMode == index, - onClick = null - ) - Spacer(modifier = Modifier.width(8.dp)) - Text(option) + 1 -> { + ThemeConfig.forceDarkMode = false + CardConfig.isUserLightModeEnabled = true + CardConfig.isUserDarkModeEnabled = false + CardConfig.save(context) + } + 0 -> { + ThemeConfig.forceDarkMode = null + CardConfig.isUserLightModeEnabled = false + CardConfig.isUserDarkModeEnabled = false + CardConfig.save(context) + } + } + showThemeModeDialog = false } - } + .padding(vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + selected = themeMode == index, + onClick = null + ) + Spacer(modifier = Modifier.width(8.dp)) + Text(option) } - }, - confirmButton = {} - ) - } - } + } + } + }, + confirmButton = {} + ) } } + } +} @Composable @@ -604,4 +628,4 @@ private fun getSliderColors(cardAlpha: Float, useCustomColors: Boolean = false): ) } } -} +} \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt index 42ac2d46..fe017583 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt @@ -1,5 +1,7 @@ package com.sukisu.ultra.ui.screen +import android.content.ClipData +import android.content.ClipboardManager import android.widget.Toast import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -44,11 +46,10 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue 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 -import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp +import androidx.core.content.getSystemService import androidx.lifecycle.compose.dropUnlessResumed import androidx.lifecycle.viewmodel.compose.viewModel import com.ramcosta.composedestinations.annotation.Destination @@ -99,8 +100,8 @@ fun AppProfileTemplateScreen( Scaffold( topBar = { - val clipboardManager = LocalClipboardManager.current val context = LocalContext.current + val clipboardManager = context.getSystemService() val showToast = fun(msg: String) { scope.launch(Dispatchers.Main) { Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() @@ -112,20 +113,20 @@ fun AppProfileTemplateScreen( scope.launch { viewModel.fetchTemplates(true) } }, onImport = { - clipboardManager.getText()?.text?.let { - if (it.isEmpty()) { + scope.launch { + val clipboardText = clipboardManager?.primaryClip?.getItemAt(0)?.text?.toString() + if (clipboardText.isNullOrEmpty()) { showToast(context.getString(R.string.app_profile_template_import_empty)) - return@let - } - scope.launch { - viewModel.importTemplates( - it, { - showToast(context.getString(R.string.app_profile_template_import_success)) - viewModel.fetchTemplates(false) - }, - showToast - ) + return@launch } + viewModel.importTemplates( + clipboardText, + { + showToast(context.getString(R.string.app_profile_template_import_success)) + viewModel.fetchTemplates(false) + }, + showToast + ) } }, onExport = { @@ -134,8 +135,8 @@ fun AppProfileTemplateScreen( { showToast(context.getString(R.string.app_profile_template_export_empty)) } - ) { - clipboardManager.setText(AnnotatedString(it)) + ) { text -> + clipboardManager?.setPrimaryClip(ClipData.newPlainText("", text)) } } }, diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt index cc4144d9..61fe77da 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt @@ -27,11 +27,14 @@ import androidx.compose.ui.zIndex import coil.compose.rememberAsyncImagePainter import androidx.compose.foundation.background import androidx.compose.ui.graphics.luminance +import androidx.compose.ui.unit.dp import java.io.File import java.io.FileOutputStream import java.io.InputStream import androidx.core.content.edit import androidx.core.net.toUri +import com.sukisu.ultra.ui.util.BackgroundTransformation +import com.sukisu.ultra.ui.util.saveTransformedBackground object ThemeConfig { var customBackgroundUri by mutableStateOf(null) @@ -88,29 +91,6 @@ private fun getLightColorScheme() = lightColorScheme( outlineVariant = Color.Black.copy(alpha = 0.12f) ) -// 复制图片到应用内部存储 -fun Context.copyImageToInternalStorage(uri: Uri): Uri? { - try { - val contentResolver: ContentResolver = contentResolver - val inputStream: InputStream = contentResolver.openInputStream(uri)!! - val fileName = "custom_background.jpg" - val file = File(filesDir, fileName) - val outputStream = FileOutputStream(file) - val buffer = ByteArray(4 * 1024) - var read: Int - while (inputStream.read(buffer).also { read = it } != -1) { - outputStream.write(buffer, 0, read) - } - outputStream.flush() - outputStream.close() - inputStream.close() - return Uri.fromFile(file) - } catch (e: Exception) { - Log.e("ImageCopy", "Failed to copy image: ${e.message}") - return null - } -} - @Composable fun KernelSUTheme( darkTheme: Boolean = when(ThemeConfig.forceDarkMode) { @@ -131,7 +111,6 @@ fun KernelSUTheme( if (darkTheme) { val originalScheme = dynamicDarkColorScheme(context) originalScheme.copy( - // 调整按钮相关颜色 primary = adjustColor(originalScheme.primary), onPrimary = adjustColor(originalScheme.onPrimary), primaryContainer = adjustColor(originalScheme.primaryContainer), @@ -242,6 +221,48 @@ fun KernelSUTheme( } } +// 复制图片到应用内部存储 +private fun Context.copyImageToInternalStorage(uri: Uri): Uri? { + try { + val contentResolver: ContentResolver = contentResolver + val inputStream: InputStream = contentResolver.openInputStream(uri)!! + val fileName = "custom_background.jpg" + val file = File(filesDir, fileName) + val outputStream = FileOutputStream(file) + val buffer = ByteArray(4 * 1024) + var read: Int + while (inputStream.read(buffer).also { read = it } != -1) { + outputStream.write(buffer, 0, read) + } + outputStream.flush() + outputStream.close() + inputStream.close() + return Uri.fromFile(file) + } catch (e: Exception) { + Log.e("ImageCopy", "Failed to copy image: ${e.message}") + return null + } +} + +// 保存变换后的背景图片到应用内部存储并更新配置 +fun Context.saveAndApplyCustomBackground(uri: Uri, transformation: BackgroundTransformation? = null) { + val finalUri = if (transformation != null) { + saveTransformedBackground(uri, transformation) + } else { + copyImageToInternalStorage(uri) + } + + getSharedPreferences("theme_prefs", Context.MODE_PRIVATE) + .edit { + putString("custom_background", finalUri?.toString()) + } + + ThemeConfig.customBackgroundUri = finalUri + CardConfig.cardElevation = 0.dp + CardConfig.isCustomBackgroundEnabled = true +} + +// 保存背景图片到应用内部存储并更新配置 fun Context.saveCustomBackground(uri: Uri?) { val newUri = uri?.let { copyImageToInternalStorage(it) } getSharedPreferences("theme_prefs", Context.MODE_PRIVATE) @@ -249,6 +270,10 @@ fun Context.saveCustomBackground(uri: Uri?) { putString("custom_background", newUri?.toString()) } ThemeConfig.customBackgroundUri = newUri + if (uri != null) { + CardConfig.cardElevation = 0.dp + CardConfig.isCustomBackgroundEnabled = true + } } fun Context.loadCustomBackground() { diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/util/BackgroundUtils.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/util/BackgroundUtils.kt new file mode 100644 index 00000000..8bbcd1de --- /dev/null +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/util/BackgroundUtils.kt @@ -0,0 +1,98 @@ +package com.sukisu.ultra.ui.util + +import android.content.ContentResolver +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Matrix +import android.net.Uri +import android.util.Log +import java.io.File +import java.io.FileOutputStream +import java.io.InputStream +import androidx.core.graphics.createBitmap + +data class BackgroundTransformation( + val scale: Float = 1f, + val offsetX: Float = 0f, + val offsetY: Float = 0f +) + +fun Context.getImageBitmap(uri: Uri): Bitmap? { + return try { + val contentResolver: ContentResolver = contentResolver + val inputStream: InputStream = contentResolver.openInputStream(uri) ?: return null + val bitmap = BitmapFactory.decodeStream(inputStream) + inputStream.close() + bitmap + } catch (e: Exception) { + Log.e("BackgroundUtils", "Failed to get image bitmap: ${e.message}") + null + } +} + +fun Context.applyTransformationToBitmap(bitmap: Bitmap, transformation: BackgroundTransformation): Bitmap { + val width = bitmap.width + val height = bitmap.height + + // 创建与屏幕比例相同的目标位图 + val displayMetrics = resources.displayMetrics + val screenWidth = displayMetrics.widthPixels + val screenHeight = displayMetrics.heightPixels + val screenRatio = screenHeight.toFloat() / screenWidth.toFloat() + + // 计算目标宽高 + val targetWidth: Int + val targetHeight: Int + if (width.toFloat() / height.toFloat() > screenRatio) { + targetHeight = height + targetWidth = (height / screenRatio).toInt() + } else { + targetWidth = width + targetHeight = (width * screenRatio).toInt() + } + + // 创建与目标相同大小的位图 + val scaledBitmap = createBitmap(targetWidth, targetHeight) + val canvas = Canvas(scaledBitmap) + + val matrix = Matrix() + + matrix.postScale(transformation.scale, transformation.scale) + + // 计算中心点 + val centerX = targetWidth / 2f + val centerY = targetHeight / 2f + + // 缩放围绕中心点 + matrix.postTranslate( + -((bitmap.width * transformation.scale - targetWidth) / 2) + transformation.offsetX, + -((bitmap.height * transformation.scale - targetHeight) / 2) + transformation.offsetY + ) + + // 将原始位图绘制到新位图上 + canvas.drawBitmap(bitmap, matrix, null) + + return scaledBitmap +} + +fun Context.saveTransformedBackground(uri: Uri, transformation: BackgroundTransformation): Uri? { + try { + val bitmap = getImageBitmap(uri) ?: return null + val transformedBitmap = applyTransformationToBitmap(bitmap, transformation) + + val fileName = "custom_background_transformed.jpg" + val file = File(filesDir, fileName) + val outputStream = FileOutputStream(file) + + transformedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outputStream) + outputStream.flush() + outputStream.close() + + return Uri.fromFile(file) + } catch (e: Exception) { + Log.e("BackgroundUtils", "Failed to save transformed image: ${e.message}") + return null + } +} \ No newline at end of file diff --git a/manager/app/src/main/res/values-ja/strings.xml b/manager/app/src/main/res/values-ja/strings.xml index c3650507..a936a1dd 100644 --- a/manager/app/src/main/res/values-ja/strings.xml +++ b/manager/app/src/main/res/values-ja/strings.xml @@ -173,7 +173,6 @@ 許可リストを復元 カスタム背景を設定 カスタム背景を設定します - カードの管理 ナビゲーションバーの透過 デフォルトに復元 Android のバージョン diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml index 03eb2b4f..509ad3da 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -262,4 +262,7 @@ 文件类型不正确,请选择 .kpm 文件 卸载 将卸载以下 kpm 模块:\n%s + 调整背景图片 + 使用双指缩放图片,单指拖动调整位置 + 无法加载图片 diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index dd27f366..594ce789 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -266,4 +266,7 @@ Uninstall The following KPM will be uninstalled: %s Disable kprobe hooks created by KernelSU, using inline hooks instead, which is similar to non-GKI kernel hooking method. + Adjust background image + Use two fingers to zoom the image, and one finger to drag it to adjust the position + Could not load image diff --git a/manager/build.gradle.kts b/manager/build.gradle.kts index 3b1373c7..0b3cd783 100644 --- a/manager/build.gradle.kts +++ b/manager/build.gradle.kts @@ -28,8 +28,8 @@ cmaker { } val androidMinSdkVersion = 26 -val androidTargetSdkVersion = 35 -val androidCompileSdkVersion = 35 +val androidTargetSdkVersion = 36 +val androidCompileSdkVersion = 36 val androidCompileNdkVersion = "28.0.13004108" val androidSourceCompatibility = JavaVersion.VERSION_21 val androidTargetCompatibility = JavaVersion.VERSION_21 diff --git a/manager/gradle/libs.versions.toml b/manager/gradle/libs.versions.toml index 15525f92..490ac686 100644 --- a/manager/gradle/libs.versions.toml +++ b/manager/gradle/libs.versions.toml @@ -1,25 +1,25 @@ [versions] -agp = "8.9.1" +agp = "8.9.2" kotlin = "2.1.10" ksp = "2.1.10-1.0.30" -compose-bom = "2025.02.00" +compose-bom = "2025.04.01" lifecycle = "2.8.7" -navigation = "2.8.7" -activity-compose = "1.10.0" -kotlinx-coroutines = "1.10.1" +navigation = "2.8.9" +activity-compose = "1.10.1" +kotlinx-coroutines = "1.10.2" coil-compose = "2.7.0" -compose-destination = "2.1.0-beta16" +compose-destination = "2.1.0" sheets-compose-dialogs = "1.3.0" markdown = "4.6.2" -webkit = "1.12.1" +webkit = "1.13.0" appiconloader-coil = "1.5.0" parcelablelist = "2.0.1" libsu = "6.0.0" apksign = "1.4" cmaker = "1.2" -compose-material = "1.7.8" -compose-material3 = "1.3.1" -compose-ui = "1.7.8" +compose-material = "1.8.0" +compose-material3 = "1.3.2" +compose-ui = "1.8.0" compose-foundation = "1.7.8" documentfile = "1.0.1"