feat: language select manually

This commit is contained in:
Miuzarte
2026-05-07 23:22:54 +08:00
parent 2adf4895fa
commit 2aa40570b8
22 changed files with 293 additions and 331 deletions

View File

@@ -1,5 +1,11 @@
# Change Log # Change Log
## 0.3.3
- 新增: 语言选择
- 改进: zh-rCN 重命名至 zh 实现繁中回退简中
- 依赖: miuix 同步至最新提交
## 0.3.2 ## 0.3.2
- 修复: 部分设备 (ColorOS) 在拉起输入法后,返回事件会经由输入法最终被发往受控机 - 修复: 部分设备 (ColorOS) 在拉起输入法后,返回事件会经由输入法最终被发往受控机

View File

@@ -56,8 +56,8 @@ android {
applicationId = "io.github.miuzarte.scrcpyforandroid" applicationId = "io.github.miuzarte.scrcpyforandroid"
minSdk = 26 minSdk = 26
targetSdk = 37 targetSdk = 37
versionCode = 28 versionCode = 29
versionName = "0.3.2" versionName = "0.3.3"
externalNativeBuild { externalNativeBuild {
cmake { cmake {

View File

@@ -7,7 +7,6 @@ import android.view.WindowManager
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -37,13 +36,14 @@ 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.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
import io.github.miuzarte.scrcpyforandroid.password.BiometricGate import io.github.miuzarte.scrcpyforandroid.password.BiometricGate
import io.github.miuzarte.scrcpyforandroid.password.PasswordCreatedState import io.github.miuzarte.scrcpyforandroid.password.PasswordCreatedState
@@ -66,19 +66,15 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import top.yukonga.miuix.kmp.basic.ButtonDefaults import top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.Card import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.DropdownEntry
import top.yukonga.miuix.kmp.basic.DropdownItem
import top.yukonga.miuix.kmp.basic.Icon import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.ListPopupDefaults
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
import top.yukonga.miuix.kmp.basic.Scaffold import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.ScrollBehavior import top.yukonga.miuix.kmp.basic.ScrollBehavior
import top.yukonga.miuix.kmp.basic.SnackbarHost import top.yukonga.miuix.kmp.basic.SnackbarHost
import top.yukonga.miuix.kmp.basic.SnackbarHostState import top.yukonga.miuix.kmp.basic.SnackbarHostState
import top.yukonga.miuix.kmp.basic.SpinnerDefaults
import top.yukonga.miuix.kmp.basic.SpinnerEntry
import top.yukonga.miuix.kmp.basic.SpinnerItemImpl
import top.yukonga.miuix.kmp.basic.Text import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TextButton import top.yukonga.miuix.kmp.basic.TextButton
import top.yukonga.miuix.kmp.basic.TextField import top.yukonga.miuix.kmp.basic.TextField
@@ -88,9 +84,9 @@ import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Close import top.yukonga.miuix.kmp.icon.extended.Close
import top.yukonga.miuix.kmp.icon.extended.More import top.yukonga.miuix.kmp.icon.extended.More
import top.yukonga.miuix.kmp.icon.extended.Ok import top.yukonga.miuix.kmp.icon.extended.Ok
import top.yukonga.miuix.kmp.menu.OverlayIconDropdownMenu
import top.yukonga.miuix.kmp.overlay.OverlayBottomSheet import top.yukonga.miuix.kmp.overlay.OverlayBottomSheet
import top.yukonga.miuix.kmp.overlay.OverlayDialog import top.yukonga.miuix.kmp.overlay.OverlayDialog
import top.yukonga.miuix.kmp.overlay.OverlayListPopup
import top.yukonga.miuix.kmp.preference.ArrowPreference import top.yukonga.miuix.kmp.preference.ArrowPreference
import top.yukonga.miuix.kmp.preference.SwitchPreference import top.yukonga.miuix.kmp.preference.SwitchPreference
import top.yukonga.miuix.kmp.theme.MiuixTheme import top.yukonga.miuix.kmp.theme.MiuixTheme
@@ -124,7 +120,6 @@ class LockscreenPasswordActivity : FragmentActivity() {
} }
MiuixTheme( MiuixTheme(
controller = themeController, controller = themeController,
smoothRounding = asBundle.smoothCorner,
) { ) {
CompositionLocalProvider( CompositionLocalProvider(
LocalSnackbarController provides snackbarController, LocalSnackbarController provides snackbarController,
@@ -181,7 +176,6 @@ private fun LockscreenPasswordScreen(
} }
val entries by PasswordRepository.entriesState.collectAsState() val entries by PasswordRepository.entriesState.collectAsState()
var showMenu by rememberSaveable { mutableStateOf(false) }
var pendingCreate by rememberSaveable { mutableStateOf(false) } var pendingCreate by rememberSaveable { mutableStateOf(false) }
var showRiskDialog by rememberSaveable { mutableStateOf(!BiometricGate.isDeviceSecure()) } var showRiskDialog by rememberSaveable { mutableStateOf(!BiometricGate.isDeviceSecure()) }
var showDisableDialog by rememberSaveable { mutableStateOf(false) } var showDisableDialog by rememberSaveable { mutableStateOf(false) }
@@ -191,10 +185,10 @@ private fun LockscreenPasswordScreen(
var editorInitialName by rememberSaveable { mutableStateOf("") } var editorInitialName by rememberSaveable { mutableStateOf("") }
DisposableEffect(Unit) { DisposableEffect(Unit) {
val observer = androidx.lifecycle.LifecycleEventObserver { _, event -> val observer = LifecycleEventObserver { _, event ->
if ( if (
event == androidx.lifecycle.Lifecycle.Event.ON_PAUSE || event == Lifecycle.Event.ON_PAUSE ||
event == androidx.lifecycle.Lifecycle.Event.ON_STOP event == Lifecycle.Event.ON_STOP
) { ) {
editorInitialName = "" editorInitialName = ""
} }
@@ -205,8 +199,8 @@ private fun LockscreenPasswordScreen(
val textCreate = stringResource(R.string.password_authenticate_create) val textCreate = stringResource(R.string.password_authenticate_create)
val textSubtitle = stringResource(R.string.password_authenticate_subtitle) val textSubtitle = stringResource(R.string.password_authenticate_subtitle)
LaunchedEffect(pendingCreate, showMenu) { LaunchedEffect(pendingCreate) {
if (!pendingCreate || showMenu) return@LaunchedEffect if (!pendingCreate) return@LaunchedEffect
if (asBundle.passwordRequireAuth) { if (asBundle.passwordRequireAuth) {
val ok = BiometricGate.authenticate( val ok = BiometricGate.authenticate(
activity = activity, activity = activity,
@@ -246,37 +240,23 @@ private fun LockscreenPasswordScreen(
} }
}, },
actions = { actions = {
Box { val textCreateNew = stringResource(R.string.password_create_new)
IconButton( OverlayIconDropdownMenu(
onClick = { showMenu = true }, entry = DropdownEntry(
) { items = listOf(
Icon( DropdownItem(
imageVector = MiuixIcons.More, text = textCreateNew,
contentDescription = stringResource(R.string.cd_more), onClick = {
)
}
val textCreateNew = stringResource(R.string.password_create_new)
OverlayListPopup(
show = showMenu,
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
alignment = PopupPositionProvider.Align.TopEnd,
onDismissRequest = { showMenu = false },
) {
ListPopupColumn {
SpinnerItemImpl(
entry = SpinnerEntry(title = textCreateNew),
entryCount = 1,
isSelected = false,
index = 0,
spinnerColors = SpinnerDefaults.spinnerColors(),
dialogMode = false,
onSelectedIndexChange = {
showMenu = false
pendingCreate = true pendingCreate = true
}, }
) )
} )
} ),
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
} }
}, },
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,

View File

@@ -1,11 +1,14 @@
package io.github.miuzarte.scrcpyforandroid package io.github.miuzarte.scrcpyforandroid
import android.content.Context
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Rect import android.graphics.Rect
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.core.content.edit
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import io.github.miuzarte.scrcpyforandroid.pages.MainScreen import io.github.miuzarte.scrcpyforandroid.pages.MainScreen
import io.github.miuzarte.scrcpyforandroid.password.BiometricGate import io.github.miuzarte.scrcpyforandroid.password.BiometricGate
@@ -13,10 +16,26 @@ import io.github.miuzarte.scrcpyforandroid.password.PasswordRepository
import io.github.miuzarte.scrcpyforandroid.password.hasAuthenticatedOrigin import io.github.miuzarte.scrcpyforandroid.password.hasAuthenticatedOrigin
import io.github.miuzarte.scrcpyforandroid.services.AppRuntime import io.github.miuzarte.scrcpyforandroid.services.AppRuntime
import io.github.miuzarte.scrcpyforandroid.services.AppScreenOn import io.github.miuzarte.scrcpyforandroid.services.AppScreenOn
import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.util.Locale
// 生物认证需要 FragmentActivity // 生物认证需要 FragmentActivity
class MainActivity : FragmentActivity() { class MainActivity : FragmentActivity() {
override fun attachBaseContext(newBase: Context) {
val languageTag = getAppLanguageTag(newBase)
val wrappedContext =
if (languageTag.isNotEmpty()) {
val config = Configuration(newBase.resources.configuration)
config.setLocale(Locale.forLanguageTag(languageTag))
newBase.createConfigurationContext(config)
} else {
newBase
}
super.attachBaseContext(wrappedContext)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
applyMainOrientationPolicy() applyMainOrientationPolicy()
@@ -27,6 +46,13 @@ class MainActivity : FragmentActivity() {
runBlocking { runBlocking {
PasswordRepository.refresh() PasswordRepository.refresh()
val cached = getAppLanguageTag(applicationContext)
if (cached.isNotEmpty()) {
val bundle = appSettings.loadBundle()
if (bundle.languageTag != cached) {
appSettings.updateBundle { it.copy(languageTag = cached) }
}
}
// 认证不可用时, 清除经认证创建的密码 // 认证不可用时, 清除经认证创建的密码
if (!BiometricGate.canAuthenticate()) { if (!BiometricGate.canAuthenticate()) {
PasswordRepository.getAll() PasswordRepository.getAll()
@@ -75,7 +101,18 @@ class MainActivity : FragmentActivity() {
return maxOf(width, height).toFloat() / minOf(width, height).toFloat() return maxOf(width, height).toFloat() / minOf(width, height).toFloat()
} }
private companion object { internal companion object {
private const val PHONE_LANDSCAPE_LOCK_ASPECT_RATIO = 16f / 9f private const val PHONE_LANDSCAPE_LOCK_ASPECT_RATIO = 16f / 9f
private const val LOCALE_PREFS = "locale_cache"
private const val KEY_LANGUAGE_TAG = "language_tag"
fun getAppLanguageTag(context: Context) =
context.getSharedPreferences(LOCALE_PREFS, MODE_PRIVATE)
.getString(KEY_LANGUAGE_TAG, "") ?: ""
fun setAppLanguageTag(context: Context, languageTag: String) =
context.getSharedPreferences(LOCALE_PREFS, MODE_PRIVATE)
.edit { putString(KEY_LANGUAGE_TAG, languageTag) }
} }
} }

View File

@@ -22,10 +22,10 @@ import top.yukonga.miuix.kmp.basic.BasicComponent
import top.yukonga.miuix.kmp.basic.BasicComponentColors import top.yukonga.miuix.kmp.basic.BasicComponentColors
import top.yukonga.miuix.kmp.basic.BasicComponentDefaults import top.yukonga.miuix.kmp.basic.BasicComponentDefaults
import top.yukonga.miuix.kmp.basic.DropdownArrowEndAction import top.yukonga.miuix.kmp.basic.DropdownArrowEndAction
import top.yukonga.miuix.kmp.basic.DropdownColors
import top.yukonga.miuix.kmp.basic.DropdownDefaults
import top.yukonga.miuix.kmp.basic.ListPopupColumn import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.PopupPositionProvider import top.yukonga.miuix.kmp.basic.PopupPositionProvider
import top.yukonga.miuix.kmp.basic.SpinnerColors
import top.yukonga.miuix.kmp.basic.SpinnerDefaults
import top.yukonga.miuix.kmp.basic.Text import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.overlay.OverlayListPopup import top.yukonga.miuix.kmp.overlay.OverlayListPopup
import top.yukonga.miuix.kmp.theme.MiuixTheme import top.yukonga.miuix.kmp.theme.MiuixTheme
@@ -44,7 +44,7 @@ fun OverlaySpinnerPreference(
titleColor: BasicComponentColors = BasicComponentDefaults.titleColor(), titleColor: BasicComponentColors = BasicComponentDefaults.titleColor(),
summary: String? = null, summary: String? = null,
summaryColor: BasicComponentColors = BasicComponentDefaults.summaryColor(), summaryColor: BasicComponentColors = BasicComponentDefaults.summaryColor(),
spinnerColors: SpinnerColors = SpinnerDefaults.spinnerColors(), spinnerColors: DropdownColors = DropdownDefaults.dropdownColors(),
startAction: @Composable (() -> Unit)? = null, startAction: @Composable (() -> Unit)? = null,
bottomAction: (@Composable () -> Unit)? = null, bottomAction: (@Composable () -> Unit)? = null,
insideMargin: PaddingValues = BasicComponentDefaults.InsideMargin, insideMargin: PaddingValues = BasicComponentDefaults.InsideMargin,
@@ -145,7 +145,7 @@ private fun OverlaySpinnerPopup(
onDismissFinished: () -> Unit, onDismissFinished: () -> Unit,
maxHeight: Dp?, maxHeight: Dp?,
hapticFeedback: HapticFeedback, hapticFeedback: HapticFeedback,
spinnerColors: SpinnerColors, spinnerColors: DropdownColors,
renderInRootScaffold: Boolean, renderInRootScaffold: Boolean,
onSelectedIndexChange: ((Int) -> Unit)?, onSelectedIndexChange: ((Int) -> Unit)?,
) { ) {

View File

@@ -22,7 +22,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import top.yukonga.miuix.kmp.basic.SpinnerColors import top.yukonga.miuix.kmp.basic.DropdownColors
import top.yukonga.miuix.kmp.basic.Text import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.theme.MiuixTheme import top.yukonga.miuix.kmp.theme.MiuixTheme
@@ -32,7 +32,7 @@ fun SpinnerItemImpl(
entryCount: Int, entryCount: Int,
isSelected: Boolean, isSelected: Boolean,
index: Int, index: Int,
spinnerColors: SpinnerColors, spinnerColors: DropdownColors,
dialogMode: Boolean = false, dialogMode: Boolean = false,
enabled: Boolean = true, enabled: Boolean = true,
onSelectedIndexChange: (Int) -> Unit, onSelectedIndexChange: (Int) -> Unit,

View File

@@ -1,6 +1,5 @@
package io.github.miuzarte.scrcpyforandroid.pages package io.github.miuzarte.scrcpyforandroid.pages
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -15,6 +14,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -72,7 +72,6 @@ import top.yukonga.miuix.kmp.blur.layerBackdrop
import top.yukonga.miuix.kmp.blur.rememberLayerBackdrop import top.yukonga.miuix.kmp.blur.rememberLayerBackdrop
import top.yukonga.miuix.kmp.blur.textureBlur import top.yukonga.miuix.kmp.blur.textureBlur
import top.yukonga.miuix.kmp.preference.ArrowPreference import top.yukonga.miuix.kmp.preference.ArrowPreference
import top.yukonga.miuix.kmp.shapes.SmoothRoundedCornerShape
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
import top.yukonga.miuix.kmp.theme.MiuixTheme.textStyles import top.yukonga.miuix.kmp.theme.MiuixTheme.textStyles
import java.util.Locale.getDefault import java.util.Locale.getDefault
@@ -208,7 +207,7 @@ private fun AboutContent(
Card( Card(
modifier = modifier.textureBlur( modifier = modifier.textureBlur(
backdrop = backdrop, backdrop = backdrop,
shape = SmoothRoundedCornerShape(16.dp), shape = RoundedCornerShape(16.dp),
blurRadius = 60f, blurRadius = 60f,
noiseCoefficient = BlurDefaults.NoiseCoefficient, noiseCoefficient = BlurDefaults.NoiseCoefficient,
colors = BlurColors(blendColors = cardBlendColors), colors = BlurColors(blendColors = cardBlendColors),
@@ -285,7 +284,7 @@ private fun AboutContent(
} }
.textureBlur( .textureBlur(
backdrop = backdrop, backdrop = backdrop,
shape = SmoothRoundedCornerShape(16.dp), shape = RoundedCornerShape(16.dp),
blurRadius = 150f, blurRadius = 150f,
noiseCoefficient = BlurDefaults.NoiseCoefficient, noiseCoefficient = BlurDefaults.NoiseCoefficient,
colors = BlurColors(blendColors = heroBlendColors), colors = BlurColors(blendColors = heroBlendColors),

View File

@@ -58,7 +58,6 @@ import io.github.miuzarte.scrcpyforandroid.services.ConnectionStateStore
import io.github.miuzarte.scrcpyforandroid.services.DeviceAdbAutoReconnectManager import io.github.miuzarte.scrcpyforandroid.services.DeviceAdbAutoReconnectManager
import io.github.miuzarte.scrcpyforandroid.services.DeviceAdbConnectionCoordinator import io.github.miuzarte.scrcpyforandroid.services.DeviceAdbConnectionCoordinator
import io.github.miuzarte.scrcpyforandroid.services.EventLogger import io.github.miuzarte.scrcpyforandroid.services.EventLogger
import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController
import io.github.miuzarte.scrcpyforandroid.ui.BlurredBar import io.github.miuzarte.scrcpyforandroid.ui.BlurredBar
import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur
import io.github.miuzarte.scrcpyforandroid.ui.contextClick import io.github.miuzarte.scrcpyforandroid.ui.contextClick
@@ -77,12 +76,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.yukonga.miuix.kmp.basic.Card import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.DropdownImpl import top.yukonga.miuix.kmp.basic.DropdownEntry
import top.yukonga.miuix.kmp.basic.DropdownItem
import top.yukonga.miuix.kmp.basic.Icon import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.ListPopupDefaults
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
import top.yukonga.miuix.kmp.basic.Scaffold import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.ScrollBehavior import top.yukonga.miuix.kmp.basic.ScrollBehavior
import top.yukonga.miuix.kmp.basic.SmallTopAppBar import top.yukonga.miuix.kmp.basic.SmallTopAppBar
@@ -91,7 +88,7 @@ import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.blur.layerBackdrop import top.yukonga.miuix.kmp.blur.layerBackdrop
import top.yukonga.miuix.kmp.icon.MiuixIcons import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.More import top.yukonga.miuix.kmp.icon.extended.More
import top.yukonga.miuix.kmp.overlay.OverlayListPopup import top.yukonga.miuix.kmp.menu.OverlayIconDropdownMenu
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
private const val PREVIEW_CARD_ITEM_KEY = "preview_card" private const val PREVIEW_CARD_ITEM_KEY = "preview_card"
@@ -117,7 +114,6 @@ internal fun DeviceTabScreen(
val viewModel: DeviceTabViewModel = viewModel(factory = viewModelFactory) val viewModel: DeviceTabViewModel = viewModel(factory = viewModelFactory)
val navigator = LocalRootNavigator.current val navigator = LocalRootNavigator.current
var showThreePointMenu by rememberSaveable { mutableStateOf(false) }
var useCompactTopAppBar by remember { mutableStateOf(false) } var useCompactTopAppBar by remember { mutableStateOf(false) }
var showTwoPaneSideAction by remember { mutableStateOf(false) } var showTwoPaneSideAction by remember { mutableStateOf(false) }
var configPanelOnLeft by remember { mutableStateOf(true) } var configPanelOnLeft by remember { mutableStateOf(true) }
@@ -145,56 +141,35 @@ internal fun DeviceTabScreen(
) )
} }
} }
Box { OverlayIconDropdownMenu(
IconButton( entry = DropdownEntry(
onClick = { showThreePointMenu = true }, items = listOf(
holdDownState = showThreePointMenu, DropdownItem(
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
}
OverlayListPopup(
show = showThreePointMenu,
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
alignment = PopupPositionProvider.Align.TopEnd,
onDismissRequest = { showThreePointMenu = false },
) {
ListPopupColumn {
DropdownImpl(
text = stringResource(R.string.device_menu_quick_sort), text = stringResource(R.string.device_menu_quick_sort),
optionSize = 3, onClick = {
isSelected = false,
index = 0,
onSelectedIndexChange = {
onOpenReorderDevices() onOpenReorderDevices()
showThreePointMenu = false
}, },
) ),
DropdownImpl( DropdownItem(
text = stringResource(R.string.device_menu_virtual_button_sort), text = stringResource(R.string.device_menu_virtual_button_sort),
optionSize = 3, onClick = {
isSelected = false,
index = 1,
onSelectedIndexChange = {
navigator.push(RootScreen.VirtualButtonOrder) navigator.push(RootScreen.VirtualButtonOrder)
showThreePointMenu = false
}, },
) ),
DropdownImpl( DropdownItem(
text = stringResource(R.string.device_menu_clear_logs), text = stringResource(R.string.device_menu_clear_logs),
optionSize = 3,
isSelected = false,
index = 2,
enabled = EventLogger.hasLogs(), enabled = EventLogger.hasLogs(),
onSelectedIndexChange = { onClick = {
EventLogger.clearLogs() EventLogger.clearLogs()
showThreePointMenu = false
}, },
) )
} )
} )
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
} }
} }
if (useCompactTopAppBar) SmallTopAppBar( if (useCompactTopAppBar) SmallTopAppBar(

View File

@@ -53,7 +53,6 @@ import io.github.miuzarte.scrcpyforandroid.scaffolds.LazyColumn
import io.github.miuzarte.scrcpyforandroid.services.AppRuntime import io.github.miuzarte.scrcpyforandroid.services.AppRuntime
import io.github.miuzarte.scrcpyforandroid.services.DirectoryDownloadSnapshot import io.github.miuzarte.scrcpyforandroid.services.DirectoryDownloadSnapshot
import io.github.miuzarte.scrcpyforandroid.services.FileManagerService import io.github.miuzarte.scrcpyforandroid.services.FileManagerService
import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController
import io.github.miuzarte.scrcpyforandroid.services.RemoteFileEntry import io.github.miuzarte.scrcpyforandroid.services.RemoteFileEntry
import io.github.miuzarte.scrcpyforandroid.services.RemoteFileKind import io.github.miuzarte.scrcpyforandroid.services.RemoteFileKind
import io.github.miuzarte.scrcpyforandroid.services.RemoteFileStat import io.github.miuzarte.scrcpyforandroid.services.RemoteFileStat
@@ -61,12 +60,10 @@ import io.github.miuzarte.scrcpyforandroid.ui.BlurredBar
import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur
import io.github.miuzarte.scrcpyforandroid.ui.rememberBlurBackdrop import io.github.miuzarte.scrcpyforandroid.ui.rememberBlurBackdrop
import top.yukonga.miuix.kmp.basic.Card import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.DropdownImpl import top.yukonga.miuix.kmp.basic.DropdownEntry
import top.yukonga.miuix.kmp.basic.HorizontalDivider import top.yukonga.miuix.kmp.basic.DropdownItem
import top.yukonga.miuix.kmp.basic.Icon import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.ListPopupDefaults
import top.yukonga.miuix.kmp.basic.PullToRefresh import top.yukonga.miuix.kmp.basic.PullToRefresh
import top.yukonga.miuix.kmp.basic.PullToRefreshState import top.yukonga.miuix.kmp.basic.PullToRefreshState
import top.yukonga.miuix.kmp.basic.Scaffold import top.yukonga.miuix.kmp.basic.Scaffold
@@ -79,9 +76,9 @@ import top.yukonga.miuix.kmp.blur.layerBackdrop
import top.yukonga.miuix.kmp.icon.MiuixIcons import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.More import top.yukonga.miuix.kmp.icon.extended.More
import top.yukonga.miuix.kmp.icon.extended.Tune import top.yukonga.miuix.kmp.icon.extended.Tune
import top.yukonga.miuix.kmp.menu.OverlayIconDropdownMenu
import top.yukonga.miuix.kmp.overlay.OverlayBottomSheet import top.yukonga.miuix.kmp.overlay.OverlayBottomSheet
import top.yukonga.miuix.kmp.overlay.OverlayDialog import top.yukonga.miuix.kmp.overlay.OverlayDialog
import top.yukonga.miuix.kmp.overlay.OverlayListPopup
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
private const val INITIAL_REMOTE_PATH = "/storage/emulated/0" private const val INITIAL_REMOTE_PATH = "/storage/emulated/0"
@@ -216,43 +213,33 @@ fun FileManagerScreen(
) )
}, },
actions = { actions = {
Box { val sortOptions = listOf(
IconButton( stringResource(R.string.fm_sort_name),
onClick = { showSortMenu = true }, stringResource(R.string.fm_sort_size),
holdDownState = showSortMenu stringResource(R.string.fm_sort_time),
) { stringResource(R.string.fm_sort_extension),
Icon( )
imageVector = MiuixIcons.Tune, val sortFieldIdx = when (sortField) {
contentDescription = stringResource(R.string.fm_cd_sort), FileManagerSortField.NAME -> 0
) FileManagerSortField.SIZE -> 1
} FileManagerSortField.TIME -> 2
OverlayListPopup( FileManagerSortField.EXTENSION -> 3
show = showSortMenu, }
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider, val dirOptions = listOf(
onDismissRequest = { showSortMenu = false }, stringResource(R.string.fm_sort_asc),
) { stringResource(R.string.fm_sort_desc),
ListPopupColumn { )
val sortOptions = listOf( val dirIdx = if (sortDescending) 1 else 0
stringResource(R.string.fm_sort_name), OverlayIconDropdownMenu(
stringResource(R.string.fm_sort_size), entries = listOf(
stringResource(R.string.fm_sort_time), DropdownEntry(
stringResource(R.string.fm_sort_extension), items = sortOptions.mapIndexed { i, option ->
) DropdownItem(
val sortFieldIdx = when (sortField) {
FileManagerSortField.NAME -> 0
FileManagerSortField.SIZE -> 1
FileManagerSortField.TIME -> 2
FileManagerSortField.EXTENSION -> 3
}
sortOptions.forEachIndexed { i, option ->
DropdownImpl(
text = option, text = option,
optionSize = sortOptions.size, selected = i == sortFieldIdx,
isSelected = i == sortFieldIdx, onClick = {
index = i,
onSelectedIndexChange = { index ->
viewModel.updateSort( viewModel.updateSort(
sortBy = when (index) { sortBy = when (i) {
1 -> FileManagerSortField.SIZE 1 -> FileManagerSortField.SIZE
2 -> FileManagerSortField.TIME 2 -> FileManagerSortField.TIME
3 -> FileManagerSortField.EXTENSION 3 -> FileManagerSortField.EXTENSION
@@ -262,67 +249,51 @@ fun FileManagerScreen(
}, },
) )
} }
HorizontalDivider(modifier = Modifier.padding(horizontal = 20.dp)) ),
val dirOptions = listOf( DropdownEntry(
stringResource(R.string.fm_sort_asc), items = dirOptions.mapIndexed { i, option ->
stringResource(R.string.fm_sort_desc), DropdownItem(
)
val dirIdx = if (sortDescending) 1 else 0
dirOptions.forEachIndexed { i, option ->
DropdownImpl(
text = option, text = option,
optionSize = dirOptions.size, selected = i == dirIdx,
isSelected = i == dirIdx, onClick = {
index = i,
onSelectedIndexChange = { index ->
viewModel.updateSort( viewModel.updateSort(
descending = index == 1 descending = i == 1
) )
}, },
) )
} }
} ),
} )
) {
Icon(
imageVector = MiuixIcons.Tune,
contentDescription = stringResource(R.string.fm_cd_sort),
)
} }
Box {
IconButton( OverlayIconDropdownMenu(
onClick = { showMenu = true }, entry = DropdownEntry(
holdDownState = showMenu, items = listOf(
) { DropdownItem(
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
}
OverlayListPopup(
show = showMenu,
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
onDismissRequest = { showMenu = false },
) {
ListPopupColumn {
DropdownImpl(
text = stringResource(R.string.fm_menu_create_folder), text = stringResource(R.string.fm_menu_create_folder),
optionSize = 2, onClick = {
isSelected = false,
index = 0,
onSelectedIndexChange = {
showMenu = false
newFolderName = "" newFolderName = ""
showCreateFolderDialog = true showCreateFolderDialog = true
}, },
) ),
DropdownImpl( DropdownItem(
text = stringResource(R.string.fm_menu_upload), text = stringResource(R.string.fm_menu_upload),
optionSize = 2, onClick = {
isSelected = false,
index = 1,
onSelectedIndexChange = {
showMenu = false
uploadLauncher.launch(arrayOf("*/*")) uploadLauncher.launch(arrayOf("*/*"))
}, },
) )
} )
} )
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
} }
}, },
) )

View File

@@ -725,7 +725,6 @@ fun MainScreen() {
MiuixTheme( MiuixTheme(
controller = themeController, controller = themeController,
smoothRounding = asBundle.smoothCorner,
) { ) {
CompositionLocalProvider( CompositionLocalProvider(
LocalEnableBlur provides asBundle.blur, LocalEnableBlur provides asBundle.blur,

View File

@@ -84,12 +84,10 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import top.yukonga.miuix.kmp.basic.ButtonDefaults import top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.Card import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.DropdownImpl import top.yukonga.miuix.kmp.basic.DropdownEntry
import top.yukonga.miuix.kmp.basic.DropdownItem
import top.yukonga.miuix.kmp.basic.Icon import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.ListPopupDefaults
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
import top.yukonga.miuix.kmp.basic.Scaffold import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.ScrollBehavior import top.yukonga.miuix.kmp.basic.ScrollBehavior
import top.yukonga.miuix.kmp.basic.SnackbarHost import top.yukonga.miuix.kmp.basic.SnackbarHost
@@ -102,9 +100,9 @@ import top.yukonga.miuix.kmp.blur.layerBackdrop
import top.yukonga.miuix.kmp.icon.MiuixIcons import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.More import top.yukonga.miuix.kmp.icon.extended.More
import top.yukonga.miuix.kmp.icon.extended.Store import top.yukonga.miuix.kmp.icon.extended.Store
import top.yukonga.miuix.kmp.menu.OverlayIconDropdownMenu
import top.yukonga.miuix.kmp.overlay.OverlayBottomSheet import top.yukonga.miuix.kmp.overlay.OverlayBottomSheet
import top.yukonga.miuix.kmp.overlay.OverlayDialog import top.yukonga.miuix.kmp.overlay.OverlayDialog
import top.yukonga.miuix.kmp.overlay.OverlayListPopup
import top.yukonga.miuix.kmp.preference.OverlayDropdownPreference import top.yukonga.miuix.kmp.preference.OverlayDropdownPreference
import top.yukonga.miuix.kmp.preference.SwitchPreference import top.yukonga.miuix.kmp.preference.SwitchPreference
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
@@ -230,35 +228,23 @@ internal fun ScrcpyAllOptionsScreen(
} }
}, },
actions = { actions = {
Box { OverlayIconDropdownMenu(
IconButton( entry = DropdownEntry(
onClick = { showProfileMenu = true }, items = listOf(
holdDownState = showProfileMenu, DropdownItem(
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.scrcpyopt_cd_profile_manage),
)
}
OverlayListPopup(
show = showProfileMenu,
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
alignment = PopupPositionProvider.Align.TopEnd,
onDismissRequest = { showProfileMenu = false },
) {
ListPopupColumn {
DropdownImpl(
text = stringResource(R.string.scrcpyopt_manage_profiles), text = stringResource(R.string.scrcpyopt_manage_profiles),
optionSize = 1, onClick = {
isSelected = false,
index = 0,
onSelectedIndexChange = {
showManageProfilesSheet = true showManageProfilesSheet = true
showProfileMenu = false showProfileMenu = false
}, },
) )
} )
} )
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.scrcpyopt_cd_profile_manage),
)
} }
}, },
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,

View File

@@ -1,11 +1,11 @@
package io.github.miuzarte.scrcpyforandroid.pages package io.github.miuzarte.scrcpyforandroid.pages
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.OpenableColumns import android.provider.OpenableColumns
import androidx.activity.compose.LocalActivity
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.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
@@ -41,6 +41,7 @@ import androidx.compose.ui.unit.Dp
import androidx.core.net.toUri import androidx.core.net.toUri
import io.github.miuzarte.scrcpyforandroid.BuildConfig import io.github.miuzarte.scrcpyforandroid.BuildConfig
import io.github.miuzarte.scrcpyforandroid.LockscreenPasswordActivity import io.github.miuzarte.scrcpyforandroid.LockscreenPasswordActivity
import io.github.miuzarte.scrcpyforandroid.MainActivity
import io.github.miuzarte.scrcpyforandroid.R import io.github.miuzarte.scrcpyforandroid.R
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
import io.github.miuzarte.scrcpyforandroid.nativecore.DirectAdbTransport import io.github.miuzarte.scrcpyforandroid.nativecore.DirectAdbTransport
@@ -87,6 +88,11 @@ import java.io.File
import kotlin.math.roundToInt import kotlin.math.roundToInt
import android.provider.Settings as AndroidSettings import android.provider.Settings as AndroidSettings
private val languages = listOf(
R.string.language_follow_system to "",
R.string.language_english to "en",
R.string.language_chinese to "zh",
)
private const val TERMINAL_FONT_RELATIVE_PATH = "terminal/font.ttf" private const val TERMINAL_FONT_RELATIVE_PATH = "terminal/font.ttf"
private val monetPaletteStyleOptions = ThemePaletteStyle.entries.map { it.name } private val monetPaletteStyleOptions = ThemePaletteStyle.entries.map { it.name }
private val monetColorSpecOptions = ThemeColorSpec.entries.map { it.name } private val monetColorSpecOptions = ThemeColorSpec.entries.map { it.name }
@@ -159,6 +165,7 @@ fun SettingsPage(
bottomInnerPadding: Dp, bottomInnerPadding: Dp,
onOpenReorderDevices: () -> Unit, onOpenReorderDevices: () -> Unit,
) { ) {
val activity = LocalActivity.current
val context = LocalContext.current val context = LocalContext.current
val updateState by AppUpdateChecker.state.collectAsState() val updateState by AppUpdateChecker.state.collectAsState()
@@ -292,6 +299,27 @@ fun SettingsPage(
item { item {
SectionSmallTitle(stringResource(R.string.section_theme)) SectionSmallTitle(stringResource(R.string.section_theme))
Card { Card {
OverlayDropdownPreference(
title = stringResource(R.string.pref_title_language),
summary = stringResource(R.string.pref_summary_language),
entries = listOf(
DropdownEntry(
items = languages.map { lang ->
DropdownItem(
text = stringResource(lang.first),
selected = lang.second == asBundle.languageTag,
onClick = {
asBundle = asBundle.copy(
languageTag = lang.second
)
MainActivity.setAppLanguageTag(context, lang.second)
activity?.recreate()
},
)
},
),
),
)
OverlayDropdownPreference( OverlayDropdownPreference(
title = stringResource(R.string.pref_title_appearance_mode), title = stringResource(R.string.pref_title_appearance_mode),
summary = stringResource(R.string.pref_summary_appearance_mode), summary = stringResource(R.string.pref_summary_appearance_mode),
@@ -393,16 +421,6 @@ fun SettingsPage(
) )
} }
} }
SwitchPreference(
title = stringResource(R.string.pref_title_smooth_corners),
summary = stringResource(R.string.pref_summary_smooth_corners),
checked = asBundle.smoothCorner,
onCheckedChange = {
asBundle = asBundle.copy(
smoothCorner = it
)
}
)
} }
} }
@@ -535,33 +553,41 @@ fun SettingsPage(
entries = listOf( entries = listOf(
DropdownEntry( DropdownEntry(
items = FullscreenVirtualButtonDock items = FullscreenVirtualButtonDock
.modeItemsResIds.map { DropdownItem(stringResource(it)) }, .modeItemsResIds.mapIndexed { index, id ->
selectedIndex = fullscreenVirtualButtonDock.modeIndex, DropdownItem(
onSelectedIndexChange = { modeIndex -> text = stringResource(id),
asBundle = asBundle.copy( selected = index == fullscreenVirtualButtonDock.modeIndex,
fullscreenVirtualButtonDock = FullscreenVirtualButtonDock onClick = {
.fromModeAndDirection( asBundle = asBundle.copy(
modeIndex = modeIndex, fullscreenVirtualButtonDock = FullscreenVirtualButtonDock
directionIndex = fullscreenVirtualButtonDock.directionIndex, .fromModeAndDirection(
) modeIndex = index,
.toStoredValue() directionIndex = fullscreenVirtualButtonDock.directionIndex,
) )
}, .toStoredValue()
)
},
)
},
), ),
DropdownEntry( DropdownEntry(
items = FullscreenVirtualButtonDock items = FullscreenVirtualButtonDock
.directionItemsResIds.map { DropdownItem(stringResource(it)) }, .directionItemsResIds.mapIndexed { index, id ->
selectedIndex = fullscreenVirtualButtonDock.directionIndex, DropdownItem(
onSelectedIndexChange = { directionIndex -> text = stringResource(id),
asBundle = asBundle.copy( selected = index == fullscreenVirtualButtonDock.directionIndex,
fullscreenVirtualButtonDock = FullscreenVirtualButtonDock onClick = {
.fromModeAndDirection( asBundle = asBundle.copy(
modeIndex = fullscreenVirtualButtonDock.modeIndex, fullscreenVirtualButtonDock = FullscreenVirtualButtonDock
directionIndex = directionIndex, .fromModeAndDirection(
) modeIndex = fullscreenVirtualButtonDock.modeIndex,
.toStoredValue() directionIndex = index,
) )
}, .toStoredValue()
)
},
)
},
), ),
), ),
title = stringResource(R.string.pref_title_virtual_button_direction), title = stringResource(R.string.pref_title_virtual_button_direction),

View File

@@ -101,7 +101,6 @@ fun StreamScreen(activity: StreamActivity) {
MiuixTheme( MiuixTheme(
controller = themeController, controller = themeController,
smoothRounding = asBundle.smoothCorner,
) { ) {
CompositionLocalProvider( CompositionLocalProvider(
LocalSnackbarController provides snackbarController, LocalSnackbarController provides snackbarController,

View File

@@ -38,8 +38,8 @@ import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@@ -52,16 +52,14 @@ import io.github.miuzarte.scrcpyforandroid.R
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
import io.github.miuzarte.scrcpyforandroid.services.AppRuntime import io.github.miuzarte.scrcpyforandroid.services.AppRuntime
import io.github.miuzarte.scrcpyforandroid.services.LocalInputService import io.github.miuzarte.scrcpyforandroid.services.LocalInputService
import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController
import io.github.miuzarte.scrcpyforandroid.ui.BlurredBar import io.github.miuzarte.scrcpyforandroid.ui.BlurredBar
import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur
import io.github.miuzarte.scrcpyforandroid.ui.contextClick import io.github.miuzarte.scrcpyforandroid.ui.contextClick
import io.github.miuzarte.scrcpyforandroid.ui.rememberBlurBackdrop import io.github.miuzarte.scrcpyforandroid.ui.rememberBlurBackdrop
import top.yukonga.miuix.kmp.basic.DropdownImpl import top.yukonga.miuix.kmp.basic.DropdownEntry
import top.yukonga.miuix.kmp.basic.DropdownItem
import top.yukonga.miuix.kmp.basic.Icon import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.ListPopupDefaults
import top.yukonga.miuix.kmp.basic.Scaffold import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.SmallTopAppBar import top.yukonga.miuix.kmp.basic.SmallTopAppBar
import top.yukonga.miuix.kmp.basic.Text import top.yukonga.miuix.kmp.basic.Text
@@ -69,8 +67,8 @@ import top.yukonga.miuix.kmp.basic.TextField
import top.yukonga.miuix.kmp.blur.layerBackdrop import top.yukonga.miuix.kmp.blur.layerBackdrop
import top.yukonga.miuix.kmp.icon.MiuixIcons import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.More import top.yukonga.miuix.kmp.icon.extended.More
import top.yukonga.miuix.kmp.menu.OverlayIconDropdownMenu
import top.yukonga.miuix.kmp.overlay.OverlayBottomSheet import top.yukonga.miuix.kmp.overlay.OverlayBottomSheet
import top.yukonga.miuix.kmp.overlay.OverlayListPopup
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
import java.io.File import java.io.File
import kotlin.math.abs import kotlin.math.abs
@@ -98,7 +96,6 @@ fun TerminalScreen(
val context = LocalContext.current val context = LocalContext.current
val blurBackdrop = rememberBlurBackdrop(LocalEnableBlur.current) val blurBackdrop = rememberBlurBackdrop(LocalEnableBlur.current)
val blurActive = blurBackdrop != null val blurActive = blurBackdrop != null
var showMenu by rememberSaveable { mutableStateOf(false) }
var showOutputSheet by rememberSaveable { mutableStateOf(false) } var showOutputSheet by rememberSaveable { mutableStateOf(false) }
var output by rememberSaveable { mutableStateOf("") } var output by rememberSaveable { mutableStateOf("") }
@@ -111,45 +108,29 @@ fun TerminalScreen(
if (blurActive) Color.Transparent if (blurActive) Color.Transparent
else colorScheme.surface, else colorScheme.surface,
actions = { actions = {
Box { OverlayIconDropdownMenu(
IconButton( entry = DropdownEntry(
onClick = { showMenu = true }, items = listOf(
holdDownState = showMenu, DropdownItem(
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
}
OverlayListPopup(
show = showMenu,
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
onDismissRequest = { showMenu = false },
) {
ListPopupColumn {
DropdownImpl(
text = stringResource(R.string.terminal_menu_free_copy), text = stringResource(R.string.terminal_menu_free_copy),
optionSize = 2,
isSelected = false,
index = 0,
enabled = output.isNotBlank(), enabled = output.isNotBlank(),
onSelectedIndexChange = { onClick = {
showMenu = false
showOutputSheet = true showOutputSheet = true
}, },
) ),
DropdownImpl( DropdownItem(
text = stringResource(R.string.terminal_menu_clear_screen), text = stringResource(R.string.terminal_menu_clear_screen),
optionSize = 2, onClick = {
isSelected = false,
index = 1,
onSelectedIndexChange = {
showMenu = false
output = "" output = ""
}, },
) )
} )
} )
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
} }
}, },
) )
@@ -175,7 +156,6 @@ fun TerminalScreen(
output = output, output = output,
onDismissRequest = { showOutputSheet = false }, onDismissRequest = { showOutputSheet = false },
onCopyAll = { onCopyAll = {
showMenu = false
LocalInputService.setClipboardText(context, output) LocalInputService.setClipboardText(context, output)
AppRuntime.snackbar(R.string.terminal_copied_all) AppRuntime.snackbar(R.string.terminal_copied_all)
}, },

View File

@@ -1,6 +1,5 @@
package io.github.miuzarte.scrcpyforandroid.password package io.github.miuzarte.scrcpyforandroid.password
import android.annotation.SuppressLint
import androidx.activity.compose.LocalActivity import androidx.activity.compose.LocalActivity
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -13,23 +12,18 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import io.github.miuzarte.scrcpyforandroid.R import io.github.miuzarte.scrcpyforandroid.R
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
import io.github.miuzarte.scrcpyforandroid.services.AppRuntime import io.github.miuzarte.scrcpyforandroid.services.AppRuntime
import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController
import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import top.yukonga.miuix.kmp.basic.DropdownDefaults
import top.yukonga.miuix.kmp.basic.DropdownItem
import top.yukonga.miuix.kmp.basic.Icon import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.ListPopupColumn import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.SpinnerDefaults
import top.yukonga.miuix.kmp.basic.SpinnerEntry
import top.yukonga.miuix.kmp.basic.SpinnerItemImpl import top.yukonga.miuix.kmp.basic.SpinnerItemImpl
import top.yukonga.miuix.kmp.basic.Text import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
@@ -37,7 +31,6 @@ import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
@Composable @Composable
fun PasswordPickerPopupContent(onDismissRequest: () -> Unit) { fun PasswordPickerPopupContent(onDismissRequest: () -> Unit) {
val fragActivity = LocalActivity.current as? FragmentActivity val fragActivity = LocalActivity.current as? FragmentActivity
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val passwordUseCase = remember { PasswordUseCase() } val passwordUseCase = remember { PasswordUseCase() }
val entries by PasswordRepository.entriesState.collectAsState() val entries by PasswordRepository.entriesState.collectAsState()
@@ -49,7 +42,7 @@ fun PasswordPickerPopupContent(onDismissRequest: () -> Unit) {
val textBurned = stringResource(R.string.password_status_burned) val textBurned = stringResource(R.string.password_status_burned)
val spinnerEntries = remember(entries) { val spinnerEntries = remember(entries) {
entries.map { entry -> entries.map { entry ->
SpinnerEntry( DropdownItem(
icon = { icon = {
Icon( Icon(
imageVector = imageVector =
@@ -123,7 +116,7 @@ fun PasswordPickerPopupContent(onDismissRequest: () -> Unit) {
entryCount = spinnerEntries.size, entryCount = spinnerEntries.size,
isSelected = false, isSelected = false,
index = index, index = index,
spinnerColors = SpinnerDefaults.spinnerColors(), spinnerColors = DropdownDefaults.dropdownColors(),
dialogMode = false, dialogMode = false,
onSelectedIndexChange = ::fillPassword, onSelectedIndexChange = ::fillPassword,
) )

View File

@@ -1,21 +1,19 @@
package io.github.miuzarte.scrcpyforandroid.scaffolds package io.github.miuzarte.scrcpyforandroid.scaffolds
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import io.github.miuzarte.scrcpyforandroid.R import io.github.miuzarte.scrcpyforandroid.R
import io.github.miuzarte.scrcpyforandroid.miuix.OverlaySpinnerPreference import io.github.miuzarte.scrcpyforandroid.miuix.OverlaySpinnerPreference
import io.github.miuzarte.scrcpyforandroid.miuix.SpinnerEntry import io.github.miuzarte.scrcpyforandroid.miuix.SpinnerEntry
import top.yukonga.miuix.kmp.basic.BasicComponentColors import top.yukonga.miuix.kmp.basic.BasicComponentColors
import top.yukonga.miuix.kmp.basic.BasicComponentDefaults import top.yukonga.miuix.kmp.basic.BasicComponentDefaults
import top.yukonga.miuix.kmp.basic.DropdownColors
import top.yukonga.miuix.kmp.basic.DropdownDefaults
import top.yukonga.miuix.kmp.basic.InfiniteProgressIndicator import top.yukonga.miuix.kmp.basic.InfiniteProgressIndicator
import top.yukonga.miuix.kmp.basic.SpinnerColors
import top.yukonga.miuix.kmp.basic.SpinnerDefaults
/** /**
* [OverlaySpinnerPreference] wrapper with fallback value display and loading state. * [OverlaySpinnerPreference] wrapper with fallback value display and loading state.
@@ -34,7 +32,7 @@ fun OverlaySpinnerWithFallback(
titleColor: BasicComponentColors = BasicComponentDefaults.titleColor(), titleColor: BasicComponentColors = BasicComponentDefaults.titleColor(),
summary: String? = null, summary: String? = null,
summaryColor: BasicComponentColors = BasicComponentDefaults.summaryColor(), summaryColor: BasicComponentColors = BasicComponentDefaults.summaryColor(),
spinnerColors: SpinnerColors = SpinnerDefaults.spinnerColors(), spinnerColors: DropdownColors = DropdownDefaults.dropdownColors(),
startAction: @Composable (() -> Unit)? = null, startAction: @Composable (() -> Unit)? = null,
bottomAction: (@Composable () -> Unit)? = null, bottomAction: (@Composable () -> Unit)? = null,
insideMargin: PaddingValues = BasicComponentDefaults.InsideMargin, insideMargin: PaddingValues = BasicComponentDefaults.InsideMargin,

View File

@@ -88,6 +88,10 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
} }
companion object { companion object {
val LANGUAGE_TAG = Pair(
stringPreferencesKey("language_tag"),
"",
)
// Theme // Theme
val THEME_BASE_INDEX = Pair( val THEME_BASE_INDEX = Pair(
intPreferencesKey("theme_base_index"), intPreferencesKey("theme_base_index"),
@@ -121,10 +125,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
booleanPreferencesKey("floating_bottom_bar_blur"), booleanPreferencesKey("floating_bottom_bar_blur"),
false, false,
) )
val SMOOTH_CORNER = Pair(
booleanPreferencesKey("smooth_corner"),
false,
)
// Scrcpy // Scrcpy
val LOW_LATENCY = Pair( val LOW_LATENCY = Pair(
@@ -302,6 +302,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
@Parcelize @Parcelize
data class Bundle( data class Bundle(
// Theme // Theme
val languageTag: String,
val themeBaseIndex: Int, val themeBaseIndex: Int,
val monet: Boolean, val monet: Boolean,
val monetSeedIndex: Int, val monetSeedIndex: Int,
@@ -310,7 +311,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
val blur: Boolean, val blur: Boolean,
val floatingBottomBar: Boolean, val floatingBottomBar: Boolean,
val floatingBottomBarBlur: Boolean, val floatingBottomBarBlur: Boolean,
val smoothCorner: Boolean,
// Scrcpy // Scrcpy
val lowLatency: Boolean, val lowLatency: Boolean,
@@ -365,6 +365,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
private val bundleFields = arrayOf<BundleField<Bundle>>( private val bundleFields = arrayOf<BundleField<Bundle>>(
// Theme // Theme
bundleField(LANGUAGE_TAG) { it.languageTag },
bundleField(THEME_BASE_INDEX) { it.themeBaseIndex }, bundleField(THEME_BASE_INDEX) { it.themeBaseIndex },
bundleField(MONET) { it.monet }, bundleField(MONET) { it.monet },
bundleField(MONET_SEED_INDEX) { it.monetSeedIndex }, bundleField(MONET_SEED_INDEX) { it.monetSeedIndex },
@@ -373,7 +374,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
bundleField(BLUR) { it.blur }, bundleField(BLUR) { it.blur },
bundleField(FLOATING_BOTTOM_BAR) { it.floatingBottomBar }, bundleField(FLOATING_BOTTOM_BAR) { it.floatingBottomBar },
bundleField(FLOATING_BOTTOM_BAR_BLUR) { it.floatingBottomBarBlur }, bundleField(FLOATING_BOTTOM_BAR_BLUR) { it.floatingBottomBarBlur },
bundleField(SMOOTH_CORNER) { it.smoothCorner },
// Scrcpy // Scrcpy
bundleField(LOW_LATENCY) { it.lowLatency }, bundleField(LOW_LATENCY) { it.lowLatency },
@@ -429,6 +429,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
private fun bundleFromPreferences(preferences: Preferences) = Bundle( private fun bundleFromPreferences(preferences: Preferences) = Bundle(
// Theme // Theme
languageTag = preferences.read(LANGUAGE_TAG),
themeBaseIndex = preferences.read(THEME_BASE_INDEX), themeBaseIndex = preferences.read(THEME_BASE_INDEX),
monet = preferences.read(MONET), monet = preferences.read(MONET),
monetSeedIndex = preferences.read(MONET_SEED_INDEX), monetSeedIndex = preferences.read(MONET_SEED_INDEX),
@@ -437,7 +438,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
blur = preferences.read(BLUR), blur = preferences.read(BLUR),
floatingBottomBar = preferences.read(FLOATING_BOTTOM_BAR), floatingBottomBar = preferences.read(FLOATING_BOTTOM_BAR),
floatingBottomBarBlur = preferences.read(FLOATING_BOTTOM_BAR_BLUR), floatingBottomBarBlur = preferences.read(FLOATING_BOTTOM_BAR_BLUR),
smoothCorner = preferences.read(SMOOTH_CORNER),
// Scrcpy // Scrcpy
lowLatency = preferences.read(LOW_LATENCY), lowLatency = preferences.read(LOW_LATENCY),

View File

@@ -17,15 +17,15 @@ import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.res.stringResource
import io.github.miuzarte.scrcpyforandroid.R import io.github.miuzarte.scrcpyforandroid.R
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
import top.yukonga.miuix.kmp.basic.DropdownColors
import top.yukonga.miuix.kmp.basic.DropdownDefaults
import top.yukonga.miuix.kmp.basic.Icon import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton import top.yukonga.miuix.kmp.basic.IconButton
import top.yukonga.miuix.kmp.basic.SpinnerColors
import top.yukonga.miuix.kmp.basic.SpinnerDefaults
import top.yukonga.miuix.kmp.basic.Text import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.icon.MiuixIcons import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Store import top.yukonga.miuix.kmp.icon.extended.Store
@@ -51,7 +51,6 @@ fun AppListBottomSheet(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onRefresh: () -> Unit, onRefresh: () -> Unit,
) { ) {
val spinnerColors = SpinnerDefaults.spinnerColors()
OverlayBottomSheet( OverlayBottomSheet(
show = show, show = show,
title = title, title = title,
@@ -98,7 +97,7 @@ fun AppListBottomSheet(
items(items = entries, key = { it.key }) { entry -> items(items = entries, key = { it.key }) { entry ->
AppListBottomSheetItem( AppListBottomSheetItem(
entry = entry, entry = entry,
spinnerColors = spinnerColors, spinnerColors = DropdownDefaults.dropdownColors(),
) )
} }
} }
@@ -110,7 +109,7 @@ fun AppListBottomSheet(
@Composable @Composable
private fun AppListBottomSheetItem( private fun AppListBottomSheetItem(
entry: AppListEntry, entry: AppListEntry,
spinnerColors: SpinnerColors, spinnerColors: DropdownColors,
) { ) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,

View File

@@ -71,12 +71,12 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import top.yukonga.miuix.kmp.basic.Button import top.yukonga.miuix.kmp.basic.Button
import top.yukonga.miuix.kmp.basic.ButtonDefaults import top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.DropdownDefaults
import top.yukonga.miuix.kmp.basic.DropdownItem
import top.yukonga.miuix.kmp.basic.Icon import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.ListPopupColumn import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.ListPopupDefaults import top.yukonga.miuix.kmp.basic.ListPopupDefaults
import top.yukonga.miuix.kmp.basic.PopupPositionProvider import top.yukonga.miuix.kmp.basic.PopupPositionProvider
import top.yukonga.miuix.kmp.basic.SpinnerDefaults
import top.yukonga.miuix.kmp.basic.SpinnerEntry
import top.yukonga.miuix.kmp.basic.SpinnerItemImpl import top.yukonga.miuix.kmp.basic.SpinnerItemImpl
import top.yukonga.miuix.kmp.basic.Text import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.icon.MiuixIcons import top.yukonga.miuix.kmp.icon.MiuixIcons
@@ -690,7 +690,7 @@ class VirtualButtonBar(
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
val spinnerItems = actions.map { action -> val spinnerItems = actions.map { action ->
val title = stringResource(action.titleResId) val title = stringResource(action.titleResId)
SpinnerEntry( DropdownItem(
icon = { icon = {
Icon( Icon(
imageVector = action.icon, imageVector = action.icon,
@@ -719,7 +719,7 @@ class VirtualButtonBar(
entryCount = spinnerItems.size, entryCount = spinnerItems.size,
isSelected = false, isSelected = false,
index = index, index = index,
spinnerColors = SpinnerDefaults.spinnerColors(), spinnerColors = DropdownDefaults.dropdownColors(),
dialogMode = false, dialogMode = false,
onSelectedIndexChange = { selectedIdx -> onSelectedIndexChange = { selectedIdx ->
haptic.confirm() haptic.confirm()

View File

@@ -246,6 +246,8 @@
<string name="pref_summary_clear_logs_on_exit">双击返回退出应用时清除日志</string> <string name="pref_summary_clear_logs_on_exit">双击返回退出应用时清除日志</string>
<string name="pref_title_hide_log_box">隐藏设备页日志框</string> <string name="pref_title_hide_log_box">隐藏设备页日志框</string>
<string name="pref_summary_hide_log_box">隐藏设备页最下方的日志框</string> <string name="pref_summary_hide_log_box">隐藏设备页最下方的日志框</string>
<string name="pref_title_language">语言</string>
<string name="pref_summary_language">应用显示语言</string>
<!-- Terminal --> <!-- Terminal -->
<string name="terminal_title">终端</string> <string name="terminal_title">终端</string>
@@ -551,4 +553,9 @@
<string name="vm_mdns_updated">mDNS 发现新端口,已更新快速设备:%1$s:%2$s -> %1$s:%3$s</string> <string name="vm_mdns_updated">mDNS 发现新端口,已更新快速设备:%1$s:%2$s -> %1$s:%3$s</string>
<string name="vm_quick_probe_success">ADB 快速探测连接成功:%1$s:%2$s</string> <string name="vm_quick_probe_success">ADB 快速探测连接成功:%1$s:%2$s</string>
<string name="vm_device_switched_profile">当前连接设备已切换为配置:%1$s</string> <string name="vm_device_switched_profile">当前连接设备已切换为配置:%1$s</string>
<string name="language_follow_system">跟随系统</string>
<string name="language_english">英语</string>
<string name="language_chinese">中文</string>
</resources> </resources>

View File

@@ -246,6 +246,8 @@
<string name="pref_summary_clear_logs_on_exit">Clear logs when double-pressing back to exit the app</string> <string name="pref_summary_clear_logs_on_exit">Clear logs when double-pressing back to exit the app</string>
<string name="pref_title_hide_log_box">Hide device page log box</string> <string name="pref_title_hide_log_box">Hide device page log box</string>
<string name="pref_summary_hide_log_box">Hide the log box at the bottom of the device page</string> <string name="pref_summary_hide_log_box">Hide the log box at the bottom of the device page</string>
<string name="pref_title_language">Language</string>
<string name="pref_summary_language">App display language</string>
<!-- Terminal --> <!-- Terminal -->
<string name="terminal_title">Terminal</string> <string name="terminal_title">Terminal</string>
@@ -551,4 +553,9 @@
<string name="vm_mdns_updated">mDNS discovered new port, updated quick device: %1$s:%2$s -> %1$s:%3$s</string> <string name="vm_mdns_updated">mDNS discovered new port, updated quick device: %1$s:%2$s -> %1$s:%3$s</string>
<string name="vm_quick_probe_success">ADB quick probe connection succeeded: %1$s:%2$s</string> <string name="vm_quick_probe_success">ADB quick probe connection succeeded: %1$s:%2$s</string>
<string name="vm_device_switched_profile">Current connected device switched to profile: %1$s</string> <string name="vm_device_switched_profile">Current connected device switched to profile: %1$s</string>
<string name="language_follow_system">Follow system</string>
<string name="language_english">English</string>
<string name="language_chinese">Chinese</string>
</resources> </resources>