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

View File

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

View File

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

View File

@@ -1,11 +1,14 @@
package io.github.miuzarte.scrcpyforandroid
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Rect
import android.os.Build
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.core.content.edit
import androidx.fragment.app.FragmentActivity
import io.github.miuzarte.scrcpyforandroid.pages.MainScreen
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.services.AppRuntime
import io.github.miuzarte.scrcpyforandroid.services.AppScreenOn
import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings
import kotlinx.coroutines.runBlocking
import java.util.Locale
// 生物认证需要 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?) {
super.onCreate(savedInstanceState)
applyMainOrientationPolicy()
@@ -27,6 +46,13 @@ class MainActivity : FragmentActivity() {
runBlocking {
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()) {
PasswordRepository.getAll()
@@ -75,7 +101,18 @@ class MainActivity : FragmentActivity() {
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 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.BasicComponentDefaults
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.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.overlay.OverlayListPopup
import top.yukonga.miuix.kmp.theme.MiuixTheme
@@ -44,7 +44,7 @@ fun OverlaySpinnerPreference(
titleColor: BasicComponentColors = BasicComponentDefaults.titleColor(),
summary: String? = null,
summaryColor: BasicComponentColors = BasicComponentDefaults.summaryColor(),
spinnerColors: SpinnerColors = SpinnerDefaults.spinnerColors(),
spinnerColors: DropdownColors = DropdownDefaults.dropdownColors(),
startAction: @Composable (() -> Unit)? = null,
bottomAction: (@Composable () -> Unit)? = null,
insideMargin: PaddingValues = BasicComponentDefaults.InsideMargin,
@@ -145,7 +145,7 @@ private fun OverlaySpinnerPopup(
onDismissFinished: () -> Unit,
maxHeight: Dp?,
hapticFeedback: HapticFeedback,
spinnerColors: SpinnerColors,
spinnerColors: DropdownColors,
renderInRootScaffold: Boolean,
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.text.font.FontWeight
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.theme.MiuixTheme
@@ -32,7 +32,7 @@ fun SpinnerItemImpl(
entryCount: Int,
isSelected: Boolean,
index: Int,
spinnerColors: SpinnerColors,
spinnerColors: DropdownColors,
dialogMode: Boolean = false,
enabled: Boolean = true,
onSelectedIndexChange: (Int) -> Unit,

View File

@@ -1,6 +1,5 @@
package io.github.miuzarte.scrcpyforandroid.pages
import android.annotation.SuppressLint
import android.content.Intent
import androidx.compose.foundation.gestures.detectTapGestures
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.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
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.textureBlur
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.textStyles
import java.util.Locale.getDefault
@@ -208,7 +207,7 @@ private fun AboutContent(
Card(
modifier = modifier.textureBlur(
backdrop = backdrop,
shape = SmoothRoundedCornerShape(16.dp),
shape = RoundedCornerShape(16.dp),
blurRadius = 60f,
noiseCoefficient = BlurDefaults.NoiseCoefficient,
colors = BlurColors(blendColors = cardBlendColors),
@@ -285,7 +284,7 @@ private fun AboutContent(
}
.textureBlur(
backdrop = backdrop,
shape = SmoothRoundedCornerShape(16.dp),
shape = RoundedCornerShape(16.dp),
blurRadius = 150f,
noiseCoefficient = BlurDefaults.NoiseCoefficient,
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.DeviceAdbConnectionCoordinator
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.LocalEnableBlur
import io.github.miuzarte.scrcpyforandroid.ui.contextClick
@@ -77,12 +76,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
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.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.ScrollBehavior
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.icon.MiuixIcons
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
private const val PREVIEW_CARD_ITEM_KEY = "preview_card"
@@ -117,7 +114,6 @@ internal fun DeviceTabScreen(
val viewModel: DeviceTabViewModel = viewModel(factory = viewModelFactory)
val navigator = LocalRootNavigator.current
var showThreePointMenu by rememberSaveable { mutableStateOf(false) }
var useCompactTopAppBar by remember { mutableStateOf(false) }
var showTwoPaneSideAction by remember { mutableStateOf(false) }
var configPanelOnLeft by remember { mutableStateOf(true) }
@@ -145,56 +141,35 @@ internal fun DeviceTabScreen(
)
}
}
Box {
IconButton(
onClick = { showThreePointMenu = true },
holdDownState = showThreePointMenu,
) {
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(
OverlayIconDropdownMenu(
entry = DropdownEntry(
items = listOf(
DropdownItem(
text = stringResource(R.string.device_menu_quick_sort),
optionSize = 3,
isSelected = false,
index = 0,
onSelectedIndexChange = {
onClick = {
onOpenReorderDevices()
showThreePointMenu = false
},
)
DropdownImpl(
),
DropdownItem(
text = stringResource(R.string.device_menu_virtual_button_sort),
optionSize = 3,
isSelected = false,
index = 1,
onSelectedIndexChange = {
onClick = {
navigator.push(RootScreen.VirtualButtonOrder)
showThreePointMenu = false
},
)
DropdownImpl(
),
DropdownItem(
text = stringResource(R.string.device_menu_clear_logs),
optionSize = 3,
isSelected = false,
index = 2,
enabled = EventLogger.hasLogs(),
onSelectedIndexChange = {
onClick = {
EventLogger.clearLogs()
showThreePointMenu = false
},
)
}
}
)
)
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
}
}
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.DirectoryDownloadSnapshot
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.RemoteFileKind
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.rememberBlurBackdrop
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.DropdownImpl
import top.yukonga.miuix.kmp.basic.HorizontalDivider
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.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.PullToRefreshState
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.extended.More
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.OverlayDialog
import top.yukonga.miuix.kmp.overlay.OverlayListPopup
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
private const val INITIAL_REMOTE_PATH = "/storage/emulated/0"
@@ -216,43 +213,33 @@ fun FileManagerScreen(
)
},
actions = {
Box {
IconButton(
onClick = { showSortMenu = true },
holdDownState = showSortMenu
) {
Icon(
imageVector = MiuixIcons.Tune,
contentDescription = stringResource(R.string.fm_cd_sort),
)
}
OverlayListPopup(
show = showSortMenu,
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
onDismissRequest = { showSortMenu = false },
) {
ListPopupColumn {
val sortOptions = listOf(
stringResource(R.string.fm_sort_name),
stringResource(R.string.fm_sort_size),
stringResource(R.string.fm_sort_time),
stringResource(R.string.fm_sort_extension),
)
val sortFieldIdx = when (sortField) {
FileManagerSortField.NAME -> 0
FileManagerSortField.SIZE -> 1
FileManagerSortField.TIME -> 2
FileManagerSortField.EXTENSION -> 3
}
sortOptions.forEachIndexed { i, option ->
DropdownImpl(
val sortOptions = listOf(
stringResource(R.string.fm_sort_name),
stringResource(R.string.fm_sort_size),
stringResource(R.string.fm_sort_time),
stringResource(R.string.fm_sort_extension),
)
val sortFieldIdx = when (sortField) {
FileManagerSortField.NAME -> 0
FileManagerSortField.SIZE -> 1
FileManagerSortField.TIME -> 2
FileManagerSortField.EXTENSION -> 3
}
val dirOptions = listOf(
stringResource(R.string.fm_sort_asc),
stringResource(R.string.fm_sort_desc),
)
val dirIdx = if (sortDescending) 1 else 0
OverlayIconDropdownMenu(
entries = listOf(
DropdownEntry(
items = sortOptions.mapIndexed { i, option ->
DropdownItem(
text = option,
optionSize = sortOptions.size,
isSelected = i == sortFieldIdx,
index = i,
onSelectedIndexChange = { index ->
selected = i == sortFieldIdx,
onClick = {
viewModel.updateSort(
sortBy = when (index) {
sortBy = when (i) {
1 -> FileManagerSortField.SIZE
2 -> FileManagerSortField.TIME
3 -> FileManagerSortField.EXTENSION
@@ -262,67 +249,51 @@ fun FileManagerScreen(
},
)
}
HorizontalDivider(modifier = Modifier.padding(horizontal = 20.dp))
val dirOptions = listOf(
stringResource(R.string.fm_sort_asc),
stringResource(R.string.fm_sort_desc),
)
val dirIdx = if (sortDescending) 1 else 0
dirOptions.forEachIndexed { i, option ->
DropdownImpl(
),
DropdownEntry(
items = dirOptions.mapIndexed { i, option ->
DropdownItem(
text = option,
optionSize = dirOptions.size,
isSelected = i == dirIdx,
index = i,
onSelectedIndexChange = { index ->
selected = i == dirIdx,
onClick = {
viewModel.updateSort(
descending = index == 1
descending = i == 1
)
},
)
}
}
}
),
)
) {
Icon(
imageVector = MiuixIcons.Tune,
contentDescription = stringResource(R.string.fm_cd_sort),
)
}
Box {
IconButton(
onClick = { showMenu = true },
holdDownState = showMenu,
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
}
OverlayListPopup(
show = showMenu,
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
onDismissRequest = { showMenu = false },
) {
ListPopupColumn {
DropdownImpl(
OverlayIconDropdownMenu(
entry = DropdownEntry(
items = listOf(
DropdownItem(
text = stringResource(R.string.fm_menu_create_folder),
optionSize = 2,
isSelected = false,
index = 0,
onSelectedIndexChange = {
showMenu = false
onClick = {
newFolderName = ""
showCreateFolderDialog = true
},
)
DropdownImpl(
),
DropdownItem(
text = stringResource(R.string.fm_menu_upload),
optionSize = 2,
isSelected = false,
index = 1,
onSelectedIndexChange = {
showMenu = false
onClick = {
uploadLauncher.launch(arrayOf("*/*"))
},
)
}
}
)
)
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
}
},
)

View File

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

View File

@@ -84,12 +84,10 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import top.yukonga.miuix.kmp.basic.ButtonDefaults
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.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.ScrollBehavior
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.extended.More
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.OverlayDialog
import top.yukonga.miuix.kmp.overlay.OverlayListPopup
import top.yukonga.miuix.kmp.preference.OverlayDropdownPreference
import top.yukonga.miuix.kmp.preference.SwitchPreference
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
@@ -230,35 +228,23 @@ internal fun ScrcpyAllOptionsScreen(
}
},
actions = {
Box {
IconButton(
onClick = { showProfileMenu = true },
holdDownState = showProfileMenu,
) {
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(
OverlayIconDropdownMenu(
entry = DropdownEntry(
items = listOf(
DropdownItem(
text = stringResource(R.string.scrcpyopt_manage_profiles),
optionSize = 1,
isSelected = false,
index = 0,
onSelectedIndexChange = {
onClick = {
showManageProfilesSheet = true
showProfileMenu = false
},
)
}
}
)
)
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.scrcpyopt_cd_profile_manage),
)
}
},
scrollBehavior = scrollBehavior,

View File

@@ -1,11 +1,11 @@
package io.github.miuzarte.scrcpyforandroid.pages
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.OpenableColumns
import androidx.activity.compose.LocalActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
@@ -41,6 +41,7 @@ import androidx.compose.ui.unit.Dp
import androidx.core.net.toUri
import io.github.miuzarte.scrcpyforandroid.BuildConfig
import io.github.miuzarte.scrcpyforandroid.LockscreenPasswordActivity
import io.github.miuzarte.scrcpyforandroid.MainActivity
import io.github.miuzarte.scrcpyforandroid.R
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
import io.github.miuzarte.scrcpyforandroid.nativecore.DirectAdbTransport
@@ -87,6 +88,11 @@ import java.io.File
import kotlin.math.roundToInt
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 val monetPaletteStyleOptions = ThemePaletteStyle.entries.map { it.name }
private val monetColorSpecOptions = ThemeColorSpec.entries.map { it.name }
@@ -159,6 +165,7 @@ fun SettingsPage(
bottomInnerPadding: Dp,
onOpenReorderDevices: () -> Unit,
) {
val activity = LocalActivity.current
val context = LocalContext.current
val updateState by AppUpdateChecker.state.collectAsState()
@@ -292,6 +299,27 @@ fun SettingsPage(
item {
SectionSmallTitle(stringResource(R.string.section_theme))
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(
title = stringResource(R.string.pref_title_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(
DropdownEntry(
items = FullscreenVirtualButtonDock
.modeItemsResIds.map { DropdownItem(stringResource(it)) },
selectedIndex = fullscreenVirtualButtonDock.modeIndex,
onSelectedIndexChange = { modeIndex ->
asBundle = asBundle.copy(
fullscreenVirtualButtonDock = FullscreenVirtualButtonDock
.fromModeAndDirection(
modeIndex = modeIndex,
directionIndex = fullscreenVirtualButtonDock.directionIndex,
)
.toStoredValue()
)
},
.modeItemsResIds.mapIndexed { index, id ->
DropdownItem(
text = stringResource(id),
selected = index == fullscreenVirtualButtonDock.modeIndex,
onClick = {
asBundle = asBundle.copy(
fullscreenVirtualButtonDock = FullscreenVirtualButtonDock
.fromModeAndDirection(
modeIndex = index,
directionIndex = fullscreenVirtualButtonDock.directionIndex,
)
.toStoredValue()
)
},
)
},
),
DropdownEntry(
items = FullscreenVirtualButtonDock
.directionItemsResIds.map { DropdownItem(stringResource(it)) },
selectedIndex = fullscreenVirtualButtonDock.directionIndex,
onSelectedIndexChange = { directionIndex ->
asBundle = asBundle.copy(
fullscreenVirtualButtonDock = FullscreenVirtualButtonDock
.fromModeAndDirection(
modeIndex = fullscreenVirtualButtonDock.modeIndex,
directionIndex = directionIndex,
)
.toStoredValue()
)
},
.directionItemsResIds.mapIndexed { index, id ->
DropdownItem(
text = stringResource(id),
selected = index == fullscreenVirtualButtonDock.directionIndex,
onClick = {
asBundle = asBundle.copy(
fullscreenVirtualButtonDock = FullscreenVirtualButtonDock
.fromModeAndDirection(
modeIndex = fullscreenVirtualButtonDock.modeIndex,
directionIndex = index,
)
.toStoredValue()
)
},
)
},
),
),
title = stringResource(R.string.pref_title_virtual_button_direction),

View File

@@ -101,7 +101,6 @@ fun StreamScreen(activity: StreamActivity) {
MiuixTheme(
controller = themeController,
smoothRounding = asBundle.smoothCorner,
) {
CompositionLocalProvider(
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.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
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.sp
@@ -52,16 +52,14 @@ import io.github.miuzarte.scrcpyforandroid.R
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
import io.github.miuzarte.scrcpyforandroid.services.AppRuntime
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.LocalEnableBlur
import io.github.miuzarte.scrcpyforandroid.ui.contextClick
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.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.SmallTopAppBar
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.icon.MiuixIcons
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.OverlayListPopup
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
import java.io.File
import kotlin.math.abs
@@ -98,7 +96,6 @@ fun TerminalScreen(
val context = LocalContext.current
val blurBackdrop = rememberBlurBackdrop(LocalEnableBlur.current)
val blurActive = blurBackdrop != null
var showMenu by rememberSaveable { mutableStateOf(false) }
var showOutputSheet by rememberSaveable { mutableStateOf(false) }
var output by rememberSaveable { mutableStateOf("") }
@@ -111,45 +108,29 @@ fun TerminalScreen(
if (blurActive) Color.Transparent
else colorScheme.surface,
actions = {
Box {
IconButton(
onClick = { showMenu = true },
holdDownState = showMenu,
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
}
OverlayListPopup(
show = showMenu,
popupPositionProvider = ListPopupDefaults.ContextMenuPositionProvider,
onDismissRequest = { showMenu = false },
) {
ListPopupColumn {
DropdownImpl(
OverlayIconDropdownMenu(
entry = DropdownEntry(
items = listOf(
DropdownItem(
text = stringResource(R.string.terminal_menu_free_copy),
optionSize = 2,
isSelected = false,
index = 0,
enabled = output.isNotBlank(),
onSelectedIndexChange = {
showMenu = false
onClick = {
showOutputSheet = true
},
)
DropdownImpl(
),
DropdownItem(
text = stringResource(R.string.terminal_menu_clear_screen),
optionSize = 2,
isSelected = false,
index = 1,
onSelectedIndexChange = {
showMenu = false
onClick = {
output = ""
},
)
}
}
)
)
) {
Icon(
imageVector = MiuixIcons.More,
contentDescription = stringResource(R.string.cd_more),
)
}
},
)
@@ -175,7 +156,6 @@ fun TerminalScreen(
output = output,
onDismissRequest = { showOutputSheet = false },
onCopyAll = {
showMenu = false
LocalInputService.setClipboardText(context, output)
AppRuntime.snackbar(R.string.terminal_copied_all)
},

View File

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

View File

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

View File

@@ -88,6 +88,10 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
}
companion object {
val LANGUAGE_TAG = Pair(
stringPreferencesKey("language_tag"),
"",
)
// Theme
val THEME_BASE_INDEX = Pair(
intPreferencesKey("theme_base_index"),
@@ -121,10 +125,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
booleanPreferencesKey("floating_bottom_bar_blur"),
false,
)
val SMOOTH_CORNER = Pair(
booleanPreferencesKey("smooth_corner"),
false,
)
// Scrcpy
val LOW_LATENCY = Pair(
@@ -302,6 +302,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
@Parcelize
data class Bundle(
// Theme
val languageTag: String,
val themeBaseIndex: Int,
val monet: Boolean,
val monetSeedIndex: Int,
@@ -310,7 +311,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
val blur: Boolean,
val floatingBottomBar: Boolean,
val floatingBottomBarBlur: Boolean,
val smoothCorner: Boolean,
// Scrcpy
val lowLatency: Boolean,
@@ -365,6 +365,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
private val bundleFields = arrayOf<BundleField<Bundle>>(
// Theme
bundleField(LANGUAGE_TAG) { it.languageTag },
bundleField(THEME_BASE_INDEX) { it.themeBaseIndex },
bundleField(MONET) { it.monet },
bundleField(MONET_SEED_INDEX) { it.monetSeedIndex },
@@ -373,7 +374,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
bundleField(BLUR) { it.blur },
bundleField(FLOATING_BOTTOM_BAR) { it.floatingBottomBar },
bundleField(FLOATING_BOTTOM_BAR_BLUR) { it.floatingBottomBarBlur },
bundleField(SMOOTH_CORNER) { it.smoothCorner },
// Scrcpy
bundleField(LOW_LATENCY) { it.lowLatency },
@@ -429,6 +429,7 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
private fun bundleFromPreferences(preferences: Preferences) = Bundle(
// Theme
languageTag = preferences.read(LANGUAGE_TAG),
themeBaseIndex = preferences.read(THEME_BASE_INDEX),
monet = preferences.read(MONET),
monetSeedIndex = preferences.read(MONET_SEED_INDEX),
@@ -437,7 +438,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
blur = preferences.read(BLUR),
floatingBottomBar = preferences.read(FLOATING_BOTTOM_BAR),
floatingBottomBarBlur = preferences.read(FLOATING_BOTTOM_BAR_BLUR),
smoothCorner = preferences.read(SMOOTH_CORNER),
// Scrcpy
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.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.res.stringResource
import io.github.miuzarte.scrcpyforandroid.R
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.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.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.extended.Store
@@ -51,7 +51,6 @@ fun AppListBottomSheet(
onDismissRequest: () -> Unit,
onRefresh: () -> Unit,
) {
val spinnerColors = SpinnerDefaults.spinnerColors()
OverlayBottomSheet(
show = show,
title = title,
@@ -98,7 +97,7 @@ fun AppListBottomSheet(
items(items = entries, key = { it.key }) { entry ->
AppListBottomSheetItem(
entry = entry,
spinnerColors = spinnerColors,
spinnerColors = DropdownDefaults.dropdownColors(),
)
}
}
@@ -110,7 +109,7 @@ fun AppListBottomSheet(
@Composable
private fun AppListBottomSheetItem(
entry: AppListEntry,
spinnerColors: SpinnerColors,
spinnerColors: DropdownColors,
) {
Row(
verticalAlignment = Alignment.CenterVertically,

View File

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

View File

@@ -246,6 +246,8 @@
<string name="pref_summary_clear_logs_on_exit">双击返回退出应用时清除日志</string>
<string name="pref_title_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 -->
<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_quick_probe_success">ADB 快速探测连接成功:%1$s:%2$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>

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_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_title_language">Language</string>
<string name="pref_summary_language">App display language</string>
<!-- Terminal -->
<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_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="language_follow_system">Follow system</string>
<string name="language_english">English</string>
<string name="language_chinese">Chinese</string>
</resources>