From e48f61a61e8d7894a2e8234be56c6fd97dd41cf4 Mon Sep 17 00:00:00 2001 From: Miuzarte <982809597@qq.com> Date: Mon, 1 Jun 2026 00:20:46 +0800 Subject: [PATCH] feat: a profile selector on top of config panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 合并多个 IP 到一个设备后不能特地为远程设备使用限制码率的配置了,外面再放一个 --- .../scrcpyforandroid/models/DeviceModels.kt | 6 +-- .../scrcpyforandroid/pages/DeviceTabScreen.kt | 43 +++++++++++++++++++ .../pages/DeviceTabViewModel.kt | 4 +- .../pages/RecordPreferencesScreen.kt | 8 +++- .../pages/ScrcpyAllOptionsScreen.kt | 21 ++++++--- 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/models/DeviceModels.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/models/DeviceModels.kt index a312e54..30789d1 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/models/DeviceModels.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/models/DeviceModels.kt @@ -136,11 +136,11 @@ class DeviceShortcuts(val devices: List): List b val updateById = id != null val updatedAddresses = when { - updateById -> { - val h = host ?: old.host + updateById && host != null -> { val p = port ?: old.port - listOf("$h:$p") + listOf("$host:$p") } + updateById -> old.addresses newPort != null -> { old.addresses.map { addr -> diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/DeviceTabScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/DeviceTabScreen.kt index 423695f..d5cf458 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/DeviceTabScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/DeviceTabScreen.kt @@ -32,6 +32,7 @@ import io.github.miuzarte.scrcpyforandroid.scaffolds.LazyColumn import io.github.miuzarte.scrcpyforandroid.scaffolds.SectionSmallTitle import io.github.miuzarte.scrcpyforandroid.scrcpy.ClientOptions import io.github.miuzarte.scrcpyforandroid.services.* +import io.github.miuzarte.scrcpyforandroid.storage.Storage.scrcpyProfiles import io.github.miuzarte.scrcpyforandroid.ui.BlurredBar import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur import io.github.miuzarte.scrcpyforandroid.ui.contextClick @@ -202,6 +203,7 @@ internal fun DeviceTabPage( val connectedScrcpyProfileId by viewModel.connectedScrcpyProfileId.collectAsState() val connectedScrcpyBundle by viewModel.connectedScrcpyBundle.collectAsState() val connectedScrcpyProfileName by viewModel.connectedScrcpyProfileName.collectAsState() + val scrcpyProfilesState by scrcpyProfiles.state.collectAsState() val canShowPreviewControls by viewModel.canShowPreviewControls.collectAsState() val virtualButtonLayout by viewModel.virtualButtonLayout.collectAsState() @@ -580,6 +582,45 @@ internal fun DeviceTabPage( } } + @Composable + fun ProfilesTabRow() { + val profileTabs = remember(scrcpyProfilesState.profiles) { + scrcpyProfilesState.profiles.map { it.name } + } + val profileIds = remember(scrcpyProfilesState.profiles) { + scrcpyProfilesState.profiles.map { it.id } + } + val selectedProfileIndex = remember(connectedScrcpyProfileId, profileIds) { + profileIds.indexOf(connectedScrcpyProfileId).coerceAtLeast(0) + } + val textGlobal = stringResource(R.string.text_global) + + TabRow( + tabs = profileTabs, + selectedTabIndex = selectedProfileIndex, + onTabSelected = { index -> + val newProfileId = profileIds.getOrNull(index) ?: return@TabRow + if (newProfileId == connectedScrcpyProfileId) return@TabRow + haptic.contextClick() + val device = currentTarget?.let { ct -> + savedShortcuts.firstOrNull { it.matchesAddress(ct) } + } + if (device != null) { + viewModel.updateShortcut(id = device.id, scrcpyProfileId = newProfileId) + } + val profileName = profileTabs.getOrElse(index) { textGlobal } + AppRuntime.snackbar(R.string.device_switched_profile, device?.name ?: "", profileName) + }, + modifier = Modifier + .padding(bottom = UiSpacing.Medium), + minWidth = 96.dp, + maxWidth = 192.dp, + height = 48.dp, + itemSpacing = UiSpacing.Medium, + ) + } + + val pageContentPadding = contentPadding val pageBottomInnerPadding = bottomInnerPadding @@ -615,12 +656,14 @@ internal fun DeviceTabPage( } item { VirtualButtonsSection() } item { + if (sessionInfo == null) ProfilesTabRow() if (useTwoPaneConfigPanel) ScrcpyConfigSectionForTwoPane() else ScrcpyConfigSection() } } else { item { SectionSmallTitle(stringResource(R.string.device_section_scrcpy)) + if (sessionInfo == null) ProfilesTabRow() if (useTwoPaneConfigPanel) ScrcpyConfigSectionForTwoPane() else ScrcpyConfigSection() } diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/DeviceTabViewModel.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/DeviceTabViewModel.kt index 09e9c97..229e502 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/DeviceTabViewModel.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/DeviceTabViewModel.kt @@ -141,7 +141,7 @@ internal class DeviceTabViewModel( ) { session, shortcuts -> val target = session.currentTarget if (session.isConnected && target != null) - shortcuts.get(target.host, target.port)?.scrcpyProfileId + shortcuts.firstOrNull { it.matchesAddress(target) }?.scrcpyProfileId ?: session.connectedScrcpyProfileId else session.connectedScrcpyProfileId @@ -907,7 +907,7 @@ internal class DeviceTabViewModel( Triple(connected, target, shortcuts) }.collect { (connected, target, shortcuts) -> if (!connected || target == null) return@collect - val boundProfileId = shortcuts.get(target.host, target.port) + val boundProfileId = shortcuts.firstOrNull { it.matchesAddress(target) } ?.scrcpyProfileId ?: ScrcpyOptions.GLOBAL_PROFILE_ID if (boundProfileId != connectionState.value.adbSession.connectedScrcpyProfileId) { connectionController.syncConnectedScrcpyProfileId(boundProfileId) diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/RecordPreferencesScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/RecordPreferencesScreen.kt index 14f46ba..adc173d 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/RecordPreferencesScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/RecordPreferencesScreen.kt @@ -48,6 +48,7 @@ internal fun RecordPreferencesScreen( profileId: String, scrcpy: Scrcpy, ) { + val haptic = LocalHapticFeedback.current val navigator = LocalRootNavigator.current val blurBackdrop = rememberBlurBackdrop(LocalEnableBlur.current) val blurActive = blurBackdrop != null @@ -63,7 +64,12 @@ internal fun RecordPreferencesScreen( if (blurActive) Color.Transparent else colorScheme.surface, navigationIcon = { - IconButton(onClick = navigator.pop) { + IconButton( + onClick = { + haptic.contextClick() + navigator.pop() + }, + ) { Icon( imageVector = Icons.AutoMirrored.Rounded.ArrowBack, contentDescription = stringResource(R.string.cd_back), diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt index 3e52d13..682b04c 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt @@ -63,6 +63,7 @@ internal fun ScrcpyAllOptionsScreen( scrollBehavior: ScrollBehavior, scrcpy: Scrcpy, ) { + val haptic = LocalHapticFeedback.current val navigator = LocalRootNavigator.current val blurBackdrop = rememberBlurBackdrop(LocalEnableBlur.current) val blurActive = blurBackdrop != null @@ -148,11 +149,11 @@ internal fun ScrcpyAllOptionsScreen( suspend fun bindCurrentConnectedDevice(profileId: String) { val target = AppRuntime.currentConnectionTarget ?: return val shortcuts = DeviceShortcuts.unmarshalFrom(qdBundleShared.quickDevicesList) - val updated = shortcuts.update( - host = target.host, - port = target.port, + val device = shortcuts.firstOrNull { it.matchesAddress(target) } + val updated = if (device != null) shortcuts.update( + id = device.id, scrcpyProfileId = profileId, - ) + ) else shortcuts if (updated != shortcuts) { quickDevices.updateBundle { bundle -> bundle.copy(quickDevicesList = updated.marshalToString()) @@ -169,7 +170,12 @@ internal fun ScrcpyAllOptionsScreen( if (blurActive) Color.Transparent else colorScheme.surface, navigationIcon = { - IconButton(onClick = navigator.pop) { + IconButton( + onClick = { + haptic.contextClick() + navigator.pop() + }, + ) { Icon( imageVector = Icons.AutoMirrored.Rounded.ArrowBack, contentDescription = stringResource(R.string.cd_back), @@ -205,6 +211,7 @@ internal fun ScrcpyAllOptionsScreen( val nextProfileId = profileIds.getOrNull(index) ?: return@TabRow if (nextProfileId == selectedProfileId) return@TabRow + haptic.contextClick() scope.launch { saveBundleForProfile(selectedProfileId, soBundleState.value) bindCurrentConnectedDevice(nextProfileId) @@ -220,8 +227,8 @@ internal fun ScrcpyAllOptionsScreen( } }, modifier = Modifier - .padding(bottom = UiSpacing.Medium) - .padding(horizontal = UiSpacing.Medium), + .padding(bottom = UiSpacing.PageHorizontal) + .padding(horizontal = UiSpacing.PageVertical), minWidth = 96.dp, maxWidth = 192.dp, height = 48.dp,