refactor: impl more scrcpy options; remove unused class/constants

- 移除不再需要的 `AppDefaults` 与 `AppPreferenceKeys`
- 实现了 `--show-touches`, `--no-power-on`, `--power-off-on-close`, `--disable-screensaver`, `--screen-off-timeout=seconds`, `--require-audio`, `--no-vd-destroy-content`, `--no-vd-system-decorations`
This commit is contained in:
Miuzarte
2026-04-10 01:19:20 +08:00
parent 6c1564f461
commit e17e1badaf
14 changed files with 371 additions and 905 deletions

16
TODO.md
View File

@@ -1,26 +1,10 @@
# TODO # TODO
## CURRENT
- Refactoring
## WIDGETS
- `SuperTextField` Click to pop a dialog with custom notes/summary
## PARAMS ## PARAMS
- orientation locking - orientation locking
- `-r, --record=file.mp4` 投屏时录制到文件 Record screen to file. The format is determined by the --record-format option if set, or by the file extension. - `-r, --record=file.mp4` 投屏时录制到文件 Record screen to file. The format is determined by the --record-format option if set, or by the file extension.
- `--record-format` 录制格式 Force recording format (mp4, mkv, m4a, mka, opus, aac, flac or wav). - `--record-format` 录制格式 Force recording format (mp4, mkv, m4a, mka, opus, aac, flac or wav).
- `-t, --show-touches` 显示受控机的物理触控 Enable "show touches" on start, restore the initial value on exit. It only shows physical touches (not clicks from scrcpy).
- `--no-power-on` 开始投屏时不唤醒屏幕 Do not power on the device on start.
- `--power-off-on-close` 结束投屏时息屏 Turn the device screen off when closing scrcpy.
- `--disable-screensaver` 投屏时禁用自动息屏 Disable screensaver while scrcpy is running.
- `--screen-off-timeout=seconds` 投屏过程中的息屏时间 Set the screen off timeout while scrcpy is running (restore the initial value on exit).
- `--require-audio` This option makes scrcpy fail if audio is enabled but does not work.
- `--no-vd-destroy-content` 投屏结束关闭虚拟显示器时不结束进程 Disable virtual display "destroy content on removal" flag. With this option, when the virtual display is closed, the running apps are moved to the main display rather than being destroyed.
- `--no-vd-system-decorations` Disable virtual display system decorations flag.
## FEATURES ## FEATURES

View File

@@ -1,82 +0,0 @@
package io.github.miuzarte.scrcpyforandroid.constants
object AppDefaults {
const val EVENT_LOG_LINES = 512
const val ADB_PORT = 5555
// Devices
const val QUICK_CONNECT_INPUT = ""
const val PAIR_HOST = ""
const val PAIR_PORT = ""
const val PAIR_CODE = ""
const val AUDIO_ENABLED = true
const val AUDIO_CODEC = "opus"
const val AUDIO_BIT_RATE_KBPS = 128
const val AUDIO_BIT_RATE_INPUT = "128"
const val VIDEO_CODEC = "h264"
const val VIDEO_BIT_RATE_MBPS = 8f
const val VIDEO_BIT_RATE_INPUT = "8.0"
const val TURN_SCREEN_OFF = false
const val NO_CONTROL = false
const val NO_VIDEO = false
const val VIDEO_SOURCE_PRESET = "display"
const val DISPLAY_ID = ""
const val CAMERA_ID = ""
const val CAMERA_FACING_PRESET = ""
const val CAMERA_SIZE_PRESET = ""
const val CAMERA_SIZE_CUSTOM = ""
const val CAMERA_AR = ""
const val CAMERA_FPS = ""
const val CAMERA_HIGH_SPEED = false
const val AUDIO_SOURCE_PRESET = "output"
const val AUDIO_SOURCE_CUSTOM = ""
const val AUDIO_DUP = false
const val NO_AUDIO_PLAYBACK = false
const val REQUIRE_AUDIO = false
const val MAX_SIZE_INPUT = ""
const val MAX_FPS_INPUT = ""
const val VIDEO_ENCODER = ""
const val VIDEO_CODEC_OPTION = ""
const val AUDIO_ENCODER = ""
const val AUDIO_CODEC_OPTION = ""
const val NEW_DISPLAY_WIDTH = ""
const val NEW_DISPLAY_HEIGHT = ""
const val NEW_DISPLAY_DPI = ""
const val CROP_WIDTH = ""
const val CROP_HEIGHT = ""
const val CROP_X = ""
const val CROP_Y = ""
// Settings
const val THEME_BASE_INDEX = 0
const val MONET = false
const val FULLSCREEN_DEBUG_INFO = false
const val SHOW_FULLSCREEN_VIRTUAL_BUTTONS = true
const val KEEP_SCREEN_ON_WHEN_STREAMING = false
const val DEVICE_PREVIEW_CARD_HEIGHT_DP = 320
const val PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT = true
const val VIRTUAL_BUTTONS_LAYOUT =
"more:1,app_switch:1,home:0,back:1,menu:0,notification:0,volume_up:0,volume_down:0,volume_mute:0,power:0,screenshot:0"
const val CUSTOM_SERVER_URI = ""
const val SERVER_REMOTE_PATH = "/data/local/tmp/scrcpy-server.jar"
const val SERVER_REMOTE_PATH_INPUT = ""
const val ADB_KEY_NAME = "scrcpy"
const val ADB_KEY_NAME_INPUT = ""
const val ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN = true
const val ADB_AUTO_RECONNECT_PAIRED_DEVICE = true
const val ADB_MDNS_LAN_DISCOVERY = true
}

View File

@@ -1,79 +0,0 @@
package io.github.miuzarte.scrcpyforandroid.constants
object AppPreferenceKeys {
const val PREFS_NAME = "scrcpy_app_prefs"
// Devices
const val QUICK_DEVICES = "quick_devices"
const val QUICK_CONNECT_INPUT = "quick_connect_input"
const val PAIR_HOST = "pair_host"
const val PAIR_PORT = "pair_port"
const val PAIR_CODE = "pair_code"
const val AUDIO_ENABLED = "audio_enabled"
const val AUDIO_CODEC = "audio_codec"
const val AUDIO_BIT_RATE_INPUT = "audio_bit_rate_input"
const val AUDIO_BIT_RATE_KBPS = "audio_bit_rate_kbps"
const val VIDEO_CODEC = "video_codec"
const val VIDEO_BIT_RATE_MBPS = "video_bit_rate_mbps"
const val VIDEO_BIT_RATE_INPUT = "video_bit_rate_input"
const val TURN_SCREEN_OFF = "turn_screen_off"
const val NO_CONTROL = "no_control"
const val NO_VIDEO = "no_video"
const val VIDEO_SOURCE_PRESET = "video_source_preset"
const val DISPLAY_ID = "display_id"
const val CAMERA_ID = "camera_id"
const val CAMERA_FACING_PRESET = "camera_facing_preset"
const val CAMERA_SIZE_PRESET = "camera_size_preset"
const val CAMERA_SIZE_CUSTOM = "camera_size_custom"
const val CAMERA_AR = "camera_ar"
const val CAMERA_FPS = "camera_fps"
const val CAMERA_HIGH_SPEED = "camera_high_speed"
const val AUDIO_SOURCE_PRESET = "audio_source_preset"
const val AUDIO_SOURCE_CUSTOM = "audio_source_custom"
const val AUDIO_DUP = "audio_dup"
const val NO_AUDIO_PLAYBACK = "no_audio_playback"
const val REQUIRE_AUDIO = "require_audio"
const val MAX_SIZE_INPUT = "max_size_input"
const val MAX_FPS_INPUT = "max_fps_input"
const val VIDEO_ENCODER = "video_encoder"
const val VIDEO_CODEC_OPTION = "video_codec_options"
const val AUDIO_ENCODER = "audio_encoder"
const val AUDIO_CODEC_OPTION = "audio_codec_options"
const val NEW_DISPLAY_WIDTH = "new_display_width"
const val NEW_DISPLAY_HEIGHT = "new_display_height"
const val NEW_DISPLAY_DPI = "new_display_dpi"
const val CROP_WIDTH = "crop_width"
const val CROP_HEIGHT = "crop_height"
const val CROP_X = "crop_x"
const val CROP_Y = "crop_y"
// Settings
const val THEME_BASE_INDEX = "theme_base_index"
const val MONET = "monet"
const val FULLSCREEN_DEBUG_INFO = "fullscreen_debug_info"
const val SHOW_FULLSCREEN_VIRTUAL_BUTTONS = "show_fullscreen_virtual_buttons"
const val KEEP_SCREEN_ON_WHEN_STREAMING = "keep_screen_on_when_streaming"
const val DEVICE_PREVIEW_CARD_HEIGHT_DP = "device_preview_card_height_dp"
const val PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT = "preview_virtual_button_show_text"
const val VIRTUAL_BUTTONS_LAYOUT = "virtual_buttons_layout"
const val CUSTOM_SERVER_URI = "custom_server_uri"
const val SERVER_REMOTE_PATH = "server_remote_path"
const val ADB_KEY_NAME = "adb_key_name"
const val ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN = "adb_pairing_auto_discover_on_dialog_open"
const val ADB_AUTO_RECONNECT_PAIRED_DEVICE = "adb_auto_reconnect_paired_device"
const val ADB_MDNS_LAN_DISCOVERY = "adb_mdns_lan_discovery"
}

View File

@@ -1,6 +1,5 @@
package io.github.miuzarte.scrcpyforandroid.constants package io.github.miuzarte.scrcpyforandroid.constants
object Defaults { object Defaults {
const val EVENT_LOG_LINES = 512
const val ADB_PORT = 5555; const val ADB_PORT = 5555;
} }

View File

@@ -1,7 +1,9 @@
package io.github.miuzarte.scrcpyforandroid.pages package io.github.miuzarte.scrcpyforandroid.pages
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.util.Log import android.util.Log
import android.view.WindowManager
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -22,6 +24,7 @@ import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.rememberSaveable 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.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade
import io.github.miuzarte.scrcpyforandroid.constants.Defaults import io.github.miuzarte.scrcpyforandroid.constants.Defaults
@@ -92,7 +95,6 @@ fun DeviceTabScreen(
snackbar: SnackbarController, snackbar: SnackbarController,
scrollBehavior: ScrollBehavior, scrollBehavior: ScrollBehavior,
onOpenVirtualButtonOrder: () -> Unit, onOpenVirtualButtonOrder: () -> Unit,
onSessionStartedChange: (Boolean) -> Unit,
onOpenReorderDevices: () -> Unit, onOpenReorderDevices: () -> Unit,
onOpenAdvancedPage: () -> Unit, onOpenAdvancedPage: () -> Unit,
onOpenFullscreenPage: (Scrcpy.Session.SessionInfo) -> Unit, onOpenFullscreenPage: (Scrcpy.Session.SessionInfo) -> Unit,
@@ -142,7 +144,6 @@ fun DeviceTabScreen(
scrcpy = scrcpy, scrcpy = scrcpy,
snackbar = snackbar, snackbar = snackbar,
scrollBehavior = scrollBehavior, scrollBehavior = scrollBehavior,
onSessionStartedChange = onSessionStartedChange,
onOpenAdvancedPage = onOpenAdvancedPage, onOpenAdvancedPage = onOpenAdvancedPage,
onOpenFullscreenPage = onOpenFullscreenPage, onOpenFullscreenPage = onOpenFullscreenPage,
) )
@@ -157,13 +158,14 @@ fun DeviceTabPage(
scrcpy: Scrcpy, scrcpy: Scrcpy,
snackbar: SnackbarController, snackbar: SnackbarController,
scrollBehavior: ScrollBehavior, scrollBehavior: ScrollBehavior,
onSessionStartedChange: (Boolean) -> Unit,
onOpenAdvancedPage: () -> Unit, onOpenAdvancedPage: () -> Unit,
onOpenFullscreenPage: (Scrcpy.Session.SessionInfo) -> Unit, onOpenFullscreenPage: (Scrcpy.Session.SessionInfo) -> Unit,
) { ) {
val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val taskScope = remember { CoroutineScope(Dispatchers.IO + SupervisorJob()) } val taskScope = remember { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
val haptics = rememberAppHaptics() val haptics = rememberAppHaptics()
val activity = remember(context) { context as? Activity }
val asBundleShared by appSettings.bundleState.collectAsState() val asBundleShared by appSettings.bundleState.collectAsState()
val asBundleSharedLatest by rememberUpdatedState(asBundleShared) val asBundleSharedLatest by rememberUpdatedState(asBundleShared)
@@ -214,6 +216,23 @@ fun DeviceTabPage(
// read only // read only
val soBundleShared by scrcpyOptions.bundleState.collectAsState() val soBundleShared by scrcpyOptions.bundleState.collectAsState()
fun setKeepScreenOn(enabled: Boolean) {
val window = activity?.window ?: return
window.decorView.post {
if (enabled) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
}
DisposableEffect(activity) {
onDispose {
setKeepScreenOn(false)
}
}
// Run adb operations on a dedicated single thread. // Run adb operations on a dedicated single thread.
// Try to avoid blocking UI/recomposition and keeps adb call ordering deterministic. // Try to avoid blocking UI/recomposition and keeps adb call ordering deterministic.
@@ -323,6 +342,7 @@ fun DeviceTabPage(
) { ) {
// Also stops scrcpy. // Also stops scrcpy.
runCatching { scrcpy.stop() } runCatching { scrcpy.stop() }
setKeepScreenOn(false)
runCatching { adbService.disconnect() } runCatching { adbService.disconnect() }
adbConnected = false adbConnected = false
currentTargetHost = "" currentTargetHost = ""
@@ -706,7 +726,6 @@ fun DeviceTabPage(
sessionInfoCodec = null sessionInfoCodec = null
sessionInfoControlEnabled = false sessionInfoControlEnabled = false
} }
onSessionStartedChange(sessionInfo != null)
} }
fun sendVirtualButtonAction(action: VirtualButtonAction) { fun sendVirtualButtonAction(action: VirtualButtonAction) {
@@ -920,6 +939,9 @@ fun DeviceTabPage(
runBusy("启动 scrcpy") { runBusy("启动 scrcpy") {
val options = scrcpyOptions.toClientOptions(soBundleShared).fix() val options = scrcpyOptions.toClientOptions(soBundleShared).fix()
val session = scrcpy.start(options) val session = scrcpy.start(options)
if (options.disableScreensaver) {
setKeepScreenOn(true)
}
sessionInfo = session.copy( sessionInfo = session.copy(
host = currentTargetHost, host = currentTargetHost,
port = currentTargetPort port = currentTargetPort
@@ -949,6 +971,7 @@ fun DeviceTabPage(
onStop = { onStop = {
runBusy("停止 scrcpy") { runBusy("停止 scrcpy") {
scrcpy.stop() scrcpy.stop()
setKeepScreenOn(false)
sessionInfo = null sessionInfo = null
statusLine = "${currentTarget!!.host}:${currentTarget.port}" statusLine = "${currentTarget!!.host}:${currentTarget.port}"
logEvent("scrcpy 已停止") logEvent("scrcpy 已停止")

View File

@@ -5,7 +5,6 @@ import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.net.Uri import android.net.Uri
import android.os.SystemClock import android.os.SystemClock
import android.view.WindowManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.activity.compose.PredictiveBackHandler import androidx.activity.compose.PredictiveBackHandler
@@ -266,27 +265,11 @@ fun MainScreen() {
} }
} }
var sessionStarted by remember { mutableStateOf(false) }
var showReorderDevices by rememberSaveable { mutableStateOf(false) } var showReorderDevices by rememberSaveable { mutableStateOf(false) }
var fullscreenOrientation by rememberSaveable { var fullscreenOrientation by rememberSaveable {
mutableIntStateOf(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) mutableIntStateOf(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
} }
val keepScreenOnWhenStreamingEnabled = asBundle.keepScreenOnWhenStreaming
// Keep-screen-on is controlled globally, so fullscreen and preview share the same behavior.
DisposableEffect(activity, keepScreenOnWhenStreamingEnabled, sessionStarted) {
val window = activity?.window
val shouldKeepScreenOn = keepScreenOnWhenStreamingEnabled && sessionStarted
if (window != null && shouldKeepScreenOn) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
onDispose {
if (window != null && shouldKeepScreenOn) {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
}
}
// Fullscreen route can force orientation based on stream ratio; all other routes are portrait. // Fullscreen route can force orientation based on stream ratio; all other routes are portrait.
LaunchedEffect(activity, currentRootScreen, fullscreenOrientation) { LaunchedEffect(activity, currentRootScreen, fullscreenOrientation) {
val targetOrientation = when (currentRootScreen) { val targetOrientation = when (currentRootScreen) {
@@ -356,7 +339,6 @@ fun MainScreen() {
snackbar = snackbarController, snackbar = snackbarController,
scrollBehavior = devicesPageScrollBehavior, scrollBehavior = devicesPageScrollBehavior,
onOpenVirtualButtonOrder = { rootBackStack.add(RootScreen.VirtualButtonOrder) }, onOpenVirtualButtonOrder = { rootBackStack.add(RootScreen.VirtualButtonOrder) },
onSessionStartedChange = { sessionStarted = it },
onOpenReorderDevices = { showReorderDevices = true }, onOpenReorderDevices = { showReorderDevices = true },
onOpenAdvancedPage = { rootBackStack.add(RootScreen.Advanced) }, onOpenAdvancedPage = { rootBackStack.add(RootScreen.Advanced) },
onOpenFullscreenPage = { session -> onOpenFullscreenPage = { session ->

View File

@@ -264,14 +264,19 @@ internal fun ScrcpyAllOptionsPage(
val videoEncoderDropdownItems = rememberSaveable(videoEncoders, listRefreshVersion) { val videoEncoderDropdownItems = rememberSaveable(videoEncoders, listRefreshVersion) {
listOf("") + videoEncoders listOf("") + videoEncoders
} }
val videoEncoderIndex = rememberSaveable(soBundle.videoEncoder, videoEncoders, listRefreshVersion) { val videoEncoderIndex =
(videoEncoders.indexOf(soBundle.videoEncoder) + 1).coerceAtLeast(0) rememberSaveable(soBundle.videoEncoder, videoEncoders, listRefreshVersion) {
} (videoEncoders.indexOf(soBundle.videoEncoder) + 1).coerceAtLeast(0)
}
val audioEncoderDropdownItems = rememberSaveable(audioEncoders, listRefreshVersion) { val audioEncoderDropdownItems = rememberSaveable(audioEncoders, listRefreshVersion) {
listOf("") + audioEncoders listOf("") + audioEncoders
} }
val audioEncoderIndex = rememberSaveable(soBundle.audioEncoder, audioEncoders, listRefreshVersion) { val audioEncoderIndex = rememberSaveable(
soBundle.audioEncoder,
audioEncoders,
listRefreshVersion
) {
(audioEncoders.indexOf(soBundle.audioEncoder) + 1).coerceAtLeast(0) (audioEncoders.indexOf(soBundle.audioEncoder) + 1).coerceAtLeast(0)
} }
@@ -419,6 +424,77 @@ internal fun ScrcpyAllOptionsPage(
}, },
// enabled = control || video, // enabled = control || video,
) )
SuperSlider(
title = "投屏过程中受控机的息屏时间",
summary = "--screen-off-timeout",
value = soBundle.screenOffTimeout.coerceAtLeast(0).toFloat(),
onValueChange = {
soBundle = soBundle.copy(
screenOffTimeout = it.roundToInt()
.takeIf { value -> value > 0 }
?.toLong() ?: -1
)
},
valueRange = 0f..600f,
steps = 600 - 1,
unit = "s",
zeroStateText = "默认",
displayText =
if (soBundle.screenOffTimeout <= 0) "默认"
else soBundle.screenOffTimeout.toString(),
inputInitialValue =
if (soBundle.screenOffTimeout <= 0) ""
else soBundle.screenOffTimeout.toString(),
inputFilter = { it.filter(Char::isDigit) },
inputValueRange = 0f..86400f,
onInputConfirm = {
soBundle = soBundle.copy(
screenOffTimeout = it.toLongOrNull()
?.takeIf { value -> value > 0 }
?: -1
)
},
)
SuperSwitch(
title = "开始投屏时不唤醒屏幕",
summary = "--no-power-on",
checked = !soBundle.powerOn,
onCheckedChange = {
soBundle = soBundle.copy(
powerOn = !it
)
},
)
SuperSwitch(
title = "结束投屏时息屏",
summary = "--power-off-on-close",
checked = soBundle.powerOffOnClose,
onCheckedChange = {
soBundle = soBundle.copy(
powerOffOnClose = it
)
},
)
SuperSwitch(
title = "显示物理触控",
summary = "--show-touches",
checked = soBundle.showTouches,
onCheckedChange = {
soBundle = soBundle.copy(
showTouches = it
)
},
)
SuperSwitch(
title = "投屏时保持本机屏幕唤醒",
summary = "--disable-screensaver",
checked = soBundle.disableScreensaver,
onCheckedChange = {
soBundle = soBundle.copy(
disableScreensaver = it
)
},
)
} }
} }
@@ -784,6 +860,26 @@ internal fun ScrcpyAllOptionsPage(
) )
}, },
) )
SuperSwitch(
title = "关闭虚拟显示器时保留内容",
summary = "--no-vd-destroy-content",
checked = !soBundle.vdDestroyContent,
onCheckedChange = {
soBundle = soBundle.copy(
vdDestroyContent = !it
)
},
)
SuperSwitch(
title = "禁用虚拟显示器系统装饰",
summary = "--no-vd-system-decorations",
checked = !soBundle.vdSystemDecorations,
onCheckedChange = {
soBundle = soBundle.copy(
vdSystemDecorations = !it
)
},
)
} }
} }

View File

@@ -194,14 +194,6 @@ fun SettingsPage(
asBundle = asBundle.copy(fullscreenDebugInfo = it) asBundle = asBundle.copy(fullscreenDebugInfo = it)
}, },
) )
SuperSwitch(
title = "投屏时保持屏幕常亮",
summary = "Scrcpy 启动后保持本机屏幕常亮,避免锁屏导致 ADB 断开",
checked = asBundle.keepScreenOnWhenStreaming,
onCheckedChange = {
asBundle = asBundle.copy(keepScreenOnWhenStreaming = it)
},
)
SuperSlider( SuperSlider(
title = "预览卡高度", title = "预览卡高度",
summary = "设备页预览卡高度", summary = "设备页预览卡高度",

View File

@@ -32,18 +32,16 @@ fun LazyColumn(
content: LazyListScope.() -> Unit, content: LazyListScope.() -> Unit,
) { ) {
val focusManager = LocalFocusManager.current val focusManager = LocalFocusManager.current
val focusClearModifier = if (clearFocusOnTap) {
Modifier.pointerInput(Unit) {
detectTapGestures(onTap = { focusManager.clearFocus() })
}
} else {
Modifier
}
LazyColumn( LazyColumn(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.then(focusClearModifier) .then(
if (clearFocusOnTap)
Modifier.pointerInput(Unit) {
detectTapGestures(onTap = { focusManager.clearFocus() })
} else Modifier
)
.overScrollVertical() .overScrollVertical()
.nestedScroll(scrollBehavior.nestedScrollConnection) .nestedScroll(scrollBehavior.nestedScrollConnection)
.padding(contentPadding), .padding(contentPadding),

View File

@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -12,6 +13,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable 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.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import top.yukonga.miuix.kmp.basic.ButtonDefaults import top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.Slider import top.yukonga.miuix.kmp.basic.Slider
@@ -131,6 +134,10 @@ private fun SliderInputDialog(
label = label, label = label,
useLabelAsPlaceholder = useLabelAsPlaceholder, useLabelAsPlaceholder = useLabelAsPlaceholder,
maxLines = 1, maxLines = 1,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done,
),
onValueChange = { newValue -> onValueChange = { newValue ->
text = inputFilter(newValue) text = inputFilter(newValue)
}, },

View File

@@ -1,528 +0,0 @@
package io.github.miuzarte.scrcpyforandroid.services
import android.content.Context
import androidx.core.content.edit
import io.github.miuzarte.scrcpyforandroid.constants.AppDefaults
import io.github.miuzarte.scrcpyforandroid.constants.AppPreferenceKeys
internal data class MainSettings(
val audioEnabled: Boolean = AppDefaults.AUDIO_ENABLED,
val audioCodec: String = AppDefaults.AUDIO_CODEC,
val videoCodec: String = AppDefaults.VIDEO_CODEC,
val themeBaseIndex: Int = AppDefaults.THEME_BASE_INDEX,
val monetEnabled: Boolean = AppDefaults.MONET,
val fullscreenDebugInfoEnabled: Boolean = AppDefaults.FULLSCREEN_DEBUG_INFO,
val showFullscreenVirtualButtons: Boolean = AppDefaults.SHOW_FULLSCREEN_VIRTUAL_BUTTONS,
val showPreviewVirtualButtonText: Boolean = AppDefaults.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT,
val keepScreenOnWhenStreamingEnabled: Boolean = AppDefaults.KEEP_SCREEN_ON_WHEN_STREAMING,
val devicePreviewCardHeightDp: Int = AppDefaults.DEVICE_PREVIEW_CARD_HEIGHT_DP,
val virtualButtonsLayout: String = AppDefaults.VIRTUAL_BUTTONS_LAYOUT,
val customServerUri: String? = AppDefaults.CUSTOM_SERVER_URI,
val serverRemotePath: String = AppDefaults.SERVER_REMOTE_PATH_INPUT,
val adbKeyName: String = AppDefaults.ADB_KEY_NAME_INPUT,
val adbPairingAutoDiscoverOnDialogOpen: Boolean =
AppDefaults.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN,
val adbAutoReconnectPairedDevice: Boolean = AppDefaults.ADB_AUTO_RECONNECT_PAIRED_DEVICE,
val adbMdnsLanDiscoveryEnabled: Boolean = AppDefaults.ADB_MDNS_LAN_DISCOVERY,
)
internal data class DevicePageSettings(
val quickConnectInput: String = AppDefaults.QUICK_CONNECT_INPUT,
val pairHost: String = AppDefaults.PAIR_HOST,
val pairPort: String = AppDefaults.PAIR_PORT,
val pairCode: String = AppDefaults.PAIR_CODE,
val audioBitRateKbps: Int = AppDefaults.AUDIO_BIT_RATE_KBPS,
val audioBitRateInput: String = AppDefaults.AUDIO_BIT_RATE_INPUT,
val videoBitRateMbps: Float = AppDefaults.VIDEO_BIT_RATE_MBPS,
val videoBitRateInput: String = AppDefaults.VIDEO_BIT_RATE_INPUT,
val turnScreenOff: Boolean = AppDefaults.TURN_SCREEN_OFF,
val noControl: Boolean = AppDefaults.NO_CONTROL,
val noVideo: Boolean = AppDefaults.NO_VIDEO,
val videoSourcePreset: String = AppDefaults.VIDEO_SOURCE_PRESET,
val displayIdInput: String = AppDefaults.DISPLAY_ID,
val cameraIdInput: String = AppDefaults.CAMERA_ID,
val cameraFacingPreset: String = AppDefaults.CAMERA_FACING_PRESET,
val cameraSizePreset: String = AppDefaults.CAMERA_SIZE_PRESET,
val cameraSizeCustom: String = AppDefaults.CAMERA_SIZE_CUSTOM,
val cameraAr: String = AppDefaults.CAMERA_AR,
val cameraFps: String = AppDefaults.CAMERA_FPS,
val cameraHighSpeed: Boolean = AppDefaults.CAMERA_HIGH_SPEED,
val audioSourcePreset: String = AppDefaults.AUDIO_SOURCE_PRESET,
val audioSourceCustom: String = AppDefaults.AUDIO_SOURCE_CUSTOM,
val audioDup: Boolean = AppDefaults.AUDIO_DUP,
val noAudioPlayback: Boolean = AppDefaults.NO_AUDIO_PLAYBACK,
val requireAudio: Boolean = AppDefaults.REQUIRE_AUDIO,
val maxSizeInput: String = AppDefaults.MAX_SIZE_INPUT,
val maxFpsInput: String = AppDefaults.MAX_FPS_INPUT,
val videoEncoder: String = AppDefaults.VIDEO_ENCODER,
val videoCodecOptions: String = AppDefaults.VIDEO_CODEC_OPTION,
val audioEncoder: String = AppDefaults.AUDIO_ENCODER,
val audioCodecOptions: String = AppDefaults.AUDIO_CODEC_OPTION,
val newDisplayWidth: String = AppDefaults.NEW_DISPLAY_WIDTH,
val newDisplayHeight: String = AppDefaults.NEW_DISPLAY_HEIGHT,
val newDisplayDpi: String = AppDefaults.NEW_DISPLAY_DPI,
val cropWidth: String = AppDefaults.CROP_WIDTH,
val cropHeight: String = AppDefaults.CROP_HEIGHT,
val cropX: String = AppDefaults.CROP_X,
val cropY: String = AppDefaults.CROP_Y,
)
internal fun loadMainSettings(context: Context): MainSettings {
val prefs = context.getSharedPreferences(
AppPreferenceKeys.PREFS_NAME,
Context.MODE_PRIVATE,
)
return MainSettings(
audioEnabled = prefs.getBoolean(
AppPreferenceKeys.AUDIO_ENABLED,
AppDefaults.AUDIO_ENABLED,
),
audioCodec = prefs.getString(
AppPreferenceKeys.AUDIO_CODEC,
AppDefaults.AUDIO_CODEC,
).orEmpty().ifBlank { AppDefaults.AUDIO_CODEC },
videoCodec = prefs.getString(
AppPreferenceKeys.VIDEO_CODEC,
AppDefaults.VIDEO_CODEC,
).orEmpty().ifBlank { AppDefaults.VIDEO_CODEC },
themeBaseIndex = prefs.getInt(
AppPreferenceKeys.THEME_BASE_INDEX,
AppDefaults.THEME_BASE_INDEX,
),
monetEnabled = prefs.getBoolean(
AppPreferenceKeys.MONET,
AppDefaults.MONET,
),
fullscreenDebugInfoEnabled = prefs.getBoolean(
AppPreferenceKeys.FULLSCREEN_DEBUG_INFO,
AppDefaults.FULLSCREEN_DEBUG_INFO,
),
showFullscreenVirtualButtons = prefs.getBoolean(
AppPreferenceKeys.SHOW_FULLSCREEN_VIRTUAL_BUTTONS,
AppDefaults.SHOW_FULLSCREEN_VIRTUAL_BUTTONS,
),
showPreviewVirtualButtonText = prefs.getBoolean(
AppPreferenceKeys.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT,
AppDefaults.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT,
),
keepScreenOnWhenStreamingEnabled = prefs.getBoolean(
AppPreferenceKeys.KEEP_SCREEN_ON_WHEN_STREAMING,
AppDefaults.KEEP_SCREEN_ON_WHEN_STREAMING,
),
devicePreviewCardHeightDp = prefs.getInt(
AppPreferenceKeys.DEVICE_PREVIEW_CARD_HEIGHT_DP,
AppDefaults.DEVICE_PREVIEW_CARD_HEIGHT_DP,
).coerceAtLeast(120),
virtualButtonsLayout = prefs.getString(
AppPreferenceKeys.VIRTUAL_BUTTONS_LAYOUT,
AppDefaults.VIRTUAL_BUTTONS_LAYOUT,
).orEmpty().ifBlank { AppDefaults.VIRTUAL_BUTTONS_LAYOUT },
customServerUri = prefs.getString(
AppPreferenceKeys.CUSTOM_SERVER_URI,
AppDefaults.CUSTOM_SERVER_URI
).orEmpty().ifBlank { null },
serverRemotePath = prefs.getString(
AppPreferenceKeys.SERVER_REMOTE_PATH,
AppDefaults.SERVER_REMOTE_PATH_INPUT,
).orEmpty(),
adbKeyName = prefs.getString(
AppPreferenceKeys.ADB_KEY_NAME,
AppDefaults.ADB_KEY_NAME_INPUT,
).orEmpty(),
adbPairingAutoDiscoverOnDialogOpen = prefs.getBoolean(
AppPreferenceKeys.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN,
AppDefaults.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN,
),
adbAutoReconnectPairedDevice = prefs.getBoolean(
AppPreferenceKeys.ADB_AUTO_RECONNECT_PAIRED_DEVICE,
AppDefaults.ADB_AUTO_RECONNECT_PAIRED_DEVICE,
),
adbMdnsLanDiscoveryEnabled = prefs.getBoolean(
AppPreferenceKeys.ADB_MDNS_LAN_DISCOVERY,
AppDefaults.ADB_MDNS_LAN_DISCOVERY,
),
)
}
internal fun saveMainSettings(context: Context, settings: MainSettings) {
context.getSharedPreferences(
AppPreferenceKeys.PREFS_NAME,
Context.MODE_PRIVATE,
).edit {
putBoolean(
AppPreferenceKeys.AUDIO_ENABLED,
settings.audioEnabled,
)
.putString(
AppPreferenceKeys.AUDIO_CODEC,
settings.audioCodec,
)
.putString(
AppPreferenceKeys.VIDEO_CODEC,
settings.videoCodec,
)
.putInt(
AppPreferenceKeys.THEME_BASE_INDEX,
settings.themeBaseIndex,
)
.putBoolean(
AppPreferenceKeys.MONET,
settings.monetEnabled,
)
.putBoolean(
AppPreferenceKeys.FULLSCREEN_DEBUG_INFO,
settings.fullscreenDebugInfoEnabled,
)
.putBoolean(
AppPreferenceKeys.SHOW_FULLSCREEN_VIRTUAL_BUTTONS,
settings.showFullscreenVirtualButtons,
)
.putBoolean(
AppPreferenceKeys.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT,
settings.showPreviewVirtualButtonText,
)
.putBoolean(
AppPreferenceKeys.KEEP_SCREEN_ON_WHEN_STREAMING,
settings.keepScreenOnWhenStreamingEnabled,
)
.putInt(
AppPreferenceKeys.DEVICE_PREVIEW_CARD_HEIGHT_DP,
settings.devicePreviewCardHeightDp.coerceAtLeast(120),
)
.putString(
AppPreferenceKeys.VIRTUAL_BUTTONS_LAYOUT,
settings.virtualButtonsLayout,
)
.putString(
AppPreferenceKeys.CUSTOM_SERVER_URI,
settings.customServerUri,
)
.putString(
AppPreferenceKeys.SERVER_REMOTE_PATH,
settings.serverRemotePath,
)
.putString(
AppPreferenceKeys.ADB_KEY_NAME,
settings.adbKeyName,
)
.putBoolean(
AppPreferenceKeys.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN,
settings.adbPairingAutoDiscoverOnDialogOpen,
)
.putBoolean(
AppPreferenceKeys.ADB_AUTO_RECONNECT_PAIRED_DEVICE,
settings.adbAutoReconnectPairedDevice,
)
.putBoolean(
AppPreferenceKeys.ADB_MDNS_LAN_DISCOVERY,
settings.adbMdnsLanDiscoveryEnabled,
)
}
}
internal fun loadDevicePageSettings(context: Context): DevicePageSettings {
val prefs = context.getSharedPreferences(
AppPreferenceKeys.PREFS_NAME,
Context.MODE_PRIVATE,
)
val audioBitRateKbps = prefs.getInt(
AppPreferenceKeys.AUDIO_BIT_RATE_KBPS,
AppDefaults.AUDIO_BIT_RATE_KBPS,
)
return DevicePageSettings(
quickConnectInput = prefs.getString(
AppPreferenceKeys.QUICK_CONNECT_INPUT,
AppDefaults.QUICK_CONNECT_INPUT,
).orEmpty(),
pairHost = AppDefaults.PAIR_HOST,
pairPort = AppDefaults.PAIR_PORT,
pairCode = AppDefaults.PAIR_CODE,
audioBitRateKbps = audioBitRateKbps,
audioBitRateInput = prefs.getString(
AppPreferenceKeys.AUDIO_BIT_RATE_INPUT,
AppDefaults.AUDIO_BIT_RATE_INPUT,
).orEmpty().ifBlank { audioBitRateKbps.toString() },
videoBitRateMbps = prefs.getFloat(
AppPreferenceKeys.VIDEO_BIT_RATE_MBPS,
AppDefaults.VIDEO_BIT_RATE_MBPS,
),
videoBitRateInput = prefs.getString(
AppPreferenceKeys.VIDEO_BIT_RATE_INPUT,
AppDefaults.VIDEO_BIT_RATE_INPUT
).orEmpty().ifBlank { AppDefaults.VIDEO_BIT_RATE_INPUT },
turnScreenOff = prefs.getBoolean(
AppPreferenceKeys.TURN_SCREEN_OFF,
AppDefaults.TURN_SCREEN_OFF,
),
noControl = prefs.getBoolean(
AppPreferenceKeys.NO_CONTROL,
AppDefaults.NO_CONTROL,
),
noVideo = prefs.getBoolean(
AppPreferenceKeys.NO_VIDEO,
AppDefaults.NO_VIDEO,
),
videoSourcePreset = prefs.getString(
AppPreferenceKeys.VIDEO_SOURCE_PRESET,
AppDefaults.VIDEO_SOURCE_PRESET,
).orEmpty().ifBlank { AppDefaults.VIDEO_SOURCE_PRESET },
displayIdInput = prefs.getString(
AppPreferenceKeys.DISPLAY_ID,
AppDefaults.DISPLAY_ID,
)
.orEmpty(),
cameraIdInput = prefs.getString(
AppPreferenceKeys.CAMERA_ID,
AppDefaults.CAMERA_ID,
)
.orEmpty(),
cameraFacingPreset = prefs.getString(
AppPreferenceKeys.CAMERA_FACING_PRESET,
AppDefaults.CAMERA_FACING_PRESET,
).orEmpty(),
cameraSizePreset = prefs.getString(
AppPreferenceKeys.CAMERA_SIZE_PRESET,
AppDefaults.CAMERA_SIZE_PRESET,
).orEmpty(),
cameraSizeCustom = prefs.getString(
AppPreferenceKeys.CAMERA_SIZE_CUSTOM,
AppDefaults.CAMERA_SIZE_CUSTOM,
).orEmpty(),
cameraAr = prefs.getString(
AppPreferenceKeys.CAMERA_AR,
AppDefaults.CAMERA_AR,
).orEmpty(),
cameraFps = prefs.getString(
AppPreferenceKeys.CAMERA_FPS,
AppDefaults.CAMERA_FPS,
).orEmpty(),
cameraHighSpeed = prefs.getBoolean(
AppPreferenceKeys.CAMERA_HIGH_SPEED,
AppDefaults.CAMERA_HIGH_SPEED,
),
audioSourcePreset = prefs.getString(
AppPreferenceKeys.AUDIO_SOURCE_PRESET,
AppDefaults.AUDIO_SOURCE_PRESET,
).orEmpty().ifBlank { AppDefaults.AUDIO_SOURCE_PRESET },
audioSourceCustom = prefs.getString(
AppPreferenceKeys.AUDIO_SOURCE_CUSTOM,
AppDefaults.AUDIO_SOURCE_CUSTOM,
).orEmpty(),
audioDup = prefs.getBoolean(
AppPreferenceKeys.AUDIO_DUP,
AppDefaults.AUDIO_DUP,
),
noAudioPlayback = prefs.getBoolean(
AppPreferenceKeys.NO_AUDIO_PLAYBACK,
AppDefaults.NO_AUDIO_PLAYBACK,
),
requireAudio = prefs.getBoolean(
AppPreferenceKeys.REQUIRE_AUDIO,
AppDefaults.REQUIRE_AUDIO,
),
maxSizeInput = prefs.getString(
AppPreferenceKeys.MAX_SIZE_INPUT,
AppDefaults.MAX_SIZE_INPUT,
)
.orEmpty(),
maxFpsInput = prefs.getString(
AppPreferenceKeys.MAX_FPS_INPUT,
AppDefaults.MAX_FPS_INPUT,
)
.orEmpty(),
videoEncoder = prefs.getString(
AppPreferenceKeys.VIDEO_ENCODER,
AppDefaults.VIDEO_ENCODER,
)
.orEmpty(),
videoCodecOptions = prefs.getString(
AppPreferenceKeys.VIDEO_CODEC_OPTION,
AppDefaults.VIDEO_CODEC_OPTION,
).orEmpty(),
audioEncoder = prefs.getString(
AppPreferenceKeys.AUDIO_ENCODER,
AppDefaults.AUDIO_ENCODER,
).orEmpty(),
audioCodecOptions = prefs.getString(
AppPreferenceKeys.AUDIO_CODEC_OPTION,
AppDefaults.AUDIO_CODEC_OPTION,
).orEmpty(),
newDisplayWidth = prefs.getString(
AppPreferenceKeys.NEW_DISPLAY_WIDTH,
AppDefaults.NEW_DISPLAY_WIDTH,
).orEmpty(),
newDisplayHeight = prefs.getString(
AppPreferenceKeys.NEW_DISPLAY_HEIGHT,
AppDefaults.NEW_DISPLAY_HEIGHT,
).orEmpty(),
newDisplayDpi = prefs.getString(
AppPreferenceKeys.NEW_DISPLAY_DPI,
AppDefaults.NEW_DISPLAY_DPI,
).orEmpty(),
cropWidth = prefs.getString(
AppPreferenceKeys.CROP_WIDTH,
AppDefaults.CROP_WIDTH,
).orEmpty(),
cropHeight = prefs.getString(
AppPreferenceKeys.CROP_HEIGHT,
AppDefaults.CROP_HEIGHT,
).orEmpty(),
cropX = prefs.getString(
AppPreferenceKeys.CROP_X,
AppDefaults.CROP_X,
).orEmpty(),
cropY = prefs.getString(
AppPreferenceKeys.CROP_Y,
AppDefaults.CROP_Y,
).orEmpty(),
)
}
internal fun saveDevicePageSettings(context: Context, settings: DevicePageSettings) {
context.getSharedPreferences(AppPreferenceKeys.PREFS_NAME, Context.MODE_PRIVATE)
.edit {
remove(AppPreferenceKeys.PAIR_HOST)
.remove(AppPreferenceKeys.PAIR_PORT)
.remove(AppPreferenceKeys.PAIR_CODE)
.putString(
AppPreferenceKeys.QUICK_CONNECT_INPUT,
settings.quickConnectInput,
)
.putInt(
AppPreferenceKeys.AUDIO_BIT_RATE_KBPS,
settings.audioBitRateKbps,
)
.putString(
AppPreferenceKeys.AUDIO_BIT_RATE_INPUT,
settings.audioBitRateInput,
)
.putFloat(
AppPreferenceKeys.VIDEO_BIT_RATE_MBPS,
settings.videoBitRateMbps,
)
.putString(
AppPreferenceKeys.VIDEO_BIT_RATE_INPUT,
settings.videoBitRateInput,
)
.putBoolean(
AppPreferenceKeys.TURN_SCREEN_OFF,
settings.turnScreenOff,
)
.putBoolean(
AppPreferenceKeys.NO_CONTROL,
settings.noControl,
)
.putBoolean(
AppPreferenceKeys.NO_VIDEO,
settings.noVideo,
)
.putString(
AppPreferenceKeys.VIDEO_SOURCE_PRESET,
settings.videoSourcePreset,
)
.putString(
AppPreferenceKeys.DISPLAY_ID,
settings.displayIdInput,
)
.putString(
AppPreferenceKeys.CAMERA_ID,
settings.cameraIdInput,
)
.putString(
AppPreferenceKeys.CAMERA_FACING_PRESET,
settings.cameraFacingPreset,
)
.putString(
AppPreferenceKeys.CAMERA_SIZE_PRESET,
settings.cameraSizePreset,
)
.putString(
AppPreferenceKeys.CAMERA_SIZE_CUSTOM,
settings.cameraSizeCustom,
)
.putString(
AppPreferenceKeys.CAMERA_AR,
settings.cameraAr,
)
.putString(
AppPreferenceKeys.CAMERA_FPS,
settings.cameraFps,
)
.putBoolean(
AppPreferenceKeys.CAMERA_HIGH_SPEED,
settings.cameraHighSpeed,
)
.putString(
AppPreferenceKeys.AUDIO_SOURCE_PRESET,
settings.audioSourcePreset,
)
.putString(
AppPreferenceKeys.AUDIO_SOURCE_CUSTOM,
settings.audioSourceCustom,
)
.putBoolean(
AppPreferenceKeys.AUDIO_DUP,
settings.audioDup,
)
.putBoolean(
AppPreferenceKeys.NO_AUDIO_PLAYBACK,
settings.noAudioPlayback,
)
.putBoolean(
AppPreferenceKeys.REQUIRE_AUDIO,
settings.requireAudio,
)
.putString(
AppPreferenceKeys.MAX_SIZE_INPUT,
settings.maxSizeInput,
)
.putString(
AppPreferenceKeys.MAX_FPS_INPUT,
settings.maxFpsInput,
)
.putString(
AppPreferenceKeys.VIDEO_ENCODER,
settings.videoEncoder,
)
.putString(
AppPreferenceKeys.VIDEO_CODEC_OPTION,
settings.videoCodecOptions,
)
.putString(
AppPreferenceKeys.AUDIO_ENCODER,
settings.audioEncoder,
)
.putString(
AppPreferenceKeys.AUDIO_CODEC_OPTION,
settings.audioCodecOptions,
)
.putString(
AppPreferenceKeys.NEW_DISPLAY_WIDTH,
settings.newDisplayWidth,
)
.putString(
AppPreferenceKeys.NEW_DISPLAY_HEIGHT,
settings.newDisplayHeight,
)
.putString(
AppPreferenceKeys.NEW_DISPLAY_DPI,
settings.newDisplayDpi,
)
.putString(
AppPreferenceKeys.CROP_WIDTH,
settings.cropWidth,
)
.putString(
AppPreferenceKeys.CROP_HEIGHT,
settings.cropHeight,
)
.putString(
AppPreferenceKeys.CROP_X,
settings.cropX,
)
.putString(
AppPreferenceKeys.CROP_Y,
settings.cropY,
)
}
}

View File

@@ -17,6 +17,8 @@ import java.util.Locale
object EventLogger { object EventLogger {
private const val LOG_TAG = "EventLogger" private const val LOG_TAG = "EventLogger"
const val MAX_LINES = 512
private val _eventLog: SnapshotStateList<String> = mutableStateListOf() private val _eventLog: SnapshotStateList<String> = mutableStateListOf()
/** /**
@@ -36,8 +38,8 @@ object EventLogger {
_eventLog.add(0, "[$timestamp] $message") _eventLog.add(0, "[$timestamp] $message")
// Rotate logs if exceeds max size // Rotate logs if exceeds max size
if (_eventLog.size > Defaults.EVENT_LOG_LINES) { if (_eventLog.size > MAX_LINES) {
_eventLog.removeRange(Defaults.EVENT_LOG_LINES, _eventLog.size) _eventLog.removeRange(MAX_LINES, _eventLog.size)
} }
// Log to Android logcat // Log to Android logcat

View File

@@ -28,10 +28,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
booleanPreferencesKey("show_fullscreen_virtual_buttons"), booleanPreferencesKey("show_fullscreen_virtual_buttons"),
true true
) )
val KEEP_SCREEN_ON_WHEN_STREAMING = Pair(
booleanPreferencesKey("keep_screen_on_when_streaming"),
false
)
val DEVICE_PREVIEW_CARD_HEIGHT_DP = Pair( val DEVICE_PREVIEW_CARD_HEIGHT_DP = Pair(
intPreferencesKey("device_preview_card_height_dp"), intPreferencesKey("device_preview_card_height_dp"),
320 320
@@ -81,7 +77,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
// Scrcpy Settings // Scrcpy Settings
val fullscreenDebugInfo by setting(FULLSCREEN_DEBUG_INFO) val fullscreenDebugInfo by setting(FULLSCREEN_DEBUG_INFO)
val showFullscreenVirtualButtons by setting(SHOW_FULLSCREEN_VIRTUAL_BUTTONS) val showFullscreenVirtualButtons by setting(SHOW_FULLSCREEN_VIRTUAL_BUTTONS)
val keepScreenOnWhenStreaming by setting(KEEP_SCREEN_ON_WHEN_STREAMING)
val devicePreviewCardHeightDp by setting(DEVICE_PREVIEW_CARD_HEIGHT_DP) val devicePreviewCardHeightDp by setting(DEVICE_PREVIEW_CARD_HEIGHT_DP)
val previewVirtualButtonShowText by setting(PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT) val previewVirtualButtonShowText by setting(PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT)
val virtualButtonsLayout by setting(VIRTUAL_BUTTONS_LAYOUT) val virtualButtonsLayout by setting(VIRTUAL_BUTTONS_LAYOUT)
@@ -103,7 +98,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
val monet: Boolean, val monet: Boolean,
val fullscreenDebugInfo: Boolean, val fullscreenDebugInfo: Boolean,
val showFullscreenVirtualButtons: Boolean, val showFullscreenVirtualButtons: Boolean,
val keepScreenOnWhenStreaming: Boolean,
val devicePreviewCardHeightDp: Int, val devicePreviewCardHeightDp: Int,
val previewVirtualButtonShowText: Boolean, val previewVirtualButtonShowText: Boolean,
val virtualButtonsLayout: String, val virtualButtonsLayout: String,
@@ -122,7 +116,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
bundleField(MONET) { bundle: Bundle -> bundle.monet }, bundleField(MONET) { bundle: Bundle -> bundle.monet },
bundleField(FULLSCREEN_DEBUG_INFO) { bundle: Bundle -> bundle.fullscreenDebugInfo }, bundleField(FULLSCREEN_DEBUG_INFO) { bundle: Bundle -> bundle.fullscreenDebugInfo },
bundleField(SHOW_FULLSCREEN_VIRTUAL_BUTTONS) { bundle: Bundle -> bundle.showFullscreenVirtualButtons }, bundleField(SHOW_FULLSCREEN_VIRTUAL_BUTTONS) { bundle: Bundle -> bundle.showFullscreenVirtualButtons },
bundleField(KEEP_SCREEN_ON_WHEN_STREAMING) { bundle: Bundle -> bundle.keepScreenOnWhenStreaming },
bundleField(DEVICE_PREVIEW_CARD_HEIGHT_DP) { bundle: Bundle -> bundle.devicePreviewCardHeightDp }, bundleField(DEVICE_PREVIEW_CARD_HEIGHT_DP) { bundle: Bundle -> bundle.devicePreviewCardHeightDp },
bundleField(PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT) { bundle: Bundle -> bundle.previewVirtualButtonShowText }, bundleField(PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT) { bundle: Bundle -> bundle.previewVirtualButtonShowText },
bundleField(VIRTUAL_BUTTONS_LAYOUT) { bundle: Bundle -> bundle.virtualButtonsLayout }, bundleField(VIRTUAL_BUTTONS_LAYOUT) { bundle: Bundle -> bundle.virtualButtonsLayout },
@@ -142,7 +135,6 @@ class AppSettings(context: Context) : Settings(context, "AppSettings") {
monet = preferences.read(MONET), monet = preferences.read(MONET),
fullscreenDebugInfo = preferences.read(FULLSCREEN_DEBUG_INFO), fullscreenDebugInfo = preferences.read(FULLSCREEN_DEBUG_INFO),
showFullscreenVirtualButtons = preferences.read(SHOW_FULLSCREEN_VIRTUAL_BUTTONS), showFullscreenVirtualButtons = preferences.read(SHOW_FULLSCREEN_VIRTUAL_BUTTONS),
keepScreenOnWhenStreaming = preferences.read(KEEP_SCREEN_ON_WHEN_STREAMING),
devicePreviewCardHeightDp = preferences.read(DEVICE_PREVIEW_CARD_HEIGHT_DP), devicePreviewCardHeightDp = preferences.read(DEVICE_PREVIEW_CARD_HEIGHT_DP),
previewVirtualButtonShowText = preferences.read(PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT), previewVirtualButtonShowText = preferences.read(PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT),
virtualButtonsLayout = preferences.read(VIRTUAL_BUTTONS_LAYOUT), virtualButtonsLayout = preferences.read(VIRTUAL_BUTTONS_LAYOUT),

View File

@@ -4,8 +4,6 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Log import android.util.Log
import androidx.core.content.edit import androidx.core.content.edit
import io.github.miuzarte.scrcpyforandroid.constants.AppDefaults
import io.github.miuzarte.scrcpyforandroid.constants.AppPreferenceKeys
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -16,7 +14,7 @@ private const val TAG = "PreferenceMigration"
*/ */
class PreferenceMigration(private val appContext: Context) { class PreferenceMigration(private val appContext: Context) {
private val appSharedPrefs: SharedPreferences by lazy { private val appSharedPrefs: SharedPreferences by lazy {
appContext.getSharedPreferences(AppPreferenceKeys.PREFS_NAME, Context.MODE_PRIVATE) appContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
} }
private val sharedPrefs: SharedPreferences by lazy { private val sharedPrefs: SharedPreferences by lazy {
@@ -80,80 +78,75 @@ class PreferenceMigration(private val appContext: Context) {
// Theme Settings // Theme Settings
migrateInt( migrateInt(
AppPreferenceKeys.THEME_BASE_INDEX, THEME_BASE_INDEX,
AppDefaults.THEME_BASE_INDEX, AppSettings.THEME_BASE_INDEX.defaultValue,
appSettings.themeBaseIndex appSettings.themeBaseIndex,
) )
migrateBoolean( migrateBoolean(
AppPreferenceKeys.MONET, MONET,
AppDefaults.MONET, AppSettings.MONET.defaultValue,
appSettings.monet appSettings.monet,
) )
// Scrcpy Settings // Scrcpy Settings
migrateBoolean( migrateBoolean(
AppPreferenceKeys.FULLSCREEN_DEBUG_INFO, FULLSCREEN_DEBUG_INFO,
AppDefaults.FULLSCREEN_DEBUG_INFO, AppSettings.FULLSCREEN_DEBUG_INFO.defaultValue,
appSettings.fullscreenDebugInfo appSettings.fullscreenDebugInfo,
) )
migrateBoolean( migrateBoolean(
AppPreferenceKeys.SHOW_FULLSCREEN_VIRTUAL_BUTTONS, SHOW_FULLSCREEN_VIRTUAL_BUTTONS,
AppDefaults.SHOW_FULLSCREEN_VIRTUAL_BUTTONS, AppSettings.SHOW_FULLSCREEN_VIRTUAL_BUTTONS.defaultValue,
appSettings.showFullscreenVirtualButtons appSettings.showFullscreenVirtualButtons,
)
migrateBoolean(
AppPreferenceKeys.KEEP_SCREEN_ON_WHEN_STREAMING,
AppDefaults.KEEP_SCREEN_ON_WHEN_STREAMING,
appSettings.keepScreenOnWhenStreaming
) )
migrateInt( migrateInt(
AppPreferenceKeys.DEVICE_PREVIEW_CARD_HEIGHT_DP, DEVICE_PREVIEW_CARD_HEIGHT_DP,
AppDefaults.DEVICE_PREVIEW_CARD_HEIGHT_DP, AppSettings.DEVICE_PREVIEW_CARD_HEIGHT_DP.defaultValue,
appSettings.devicePreviewCardHeightDp appSettings.devicePreviewCardHeightDp,
) )
migrateBoolean( migrateBoolean(
AppPreferenceKeys.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT, PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT,
AppDefaults.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT, AppSettings.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT.defaultValue,
appSettings.previewVirtualButtonShowText appSettings.previewVirtualButtonShowText,
) )
migrateString( migrateString(
AppPreferenceKeys.VIRTUAL_BUTTONS_LAYOUT, VIRTUAL_BUTTONS_LAYOUT,
AppDefaults.VIRTUAL_BUTTONS_LAYOUT, AppSettings.VIRTUAL_BUTTONS_LAYOUT.defaultValue,
appSettings.virtualButtonsLayout appSettings.virtualButtonsLayout,
) )
// Scrcpy Server Settings // Scrcpy Server Settings
migrateString( migrateString(
AppPreferenceKeys.CUSTOM_SERVER_URI, CUSTOM_SERVER_URI,
AppDefaults.CUSTOM_SERVER_URI ?: "", AppSettings.CUSTOM_SERVER_URI.defaultValue,
appSettings.customServerUri appSettings.customServerUri,
) )
migrateString( migrateString(
AppPreferenceKeys.SERVER_REMOTE_PATH, SERVER_REMOTE_PATH,
AppDefaults.SERVER_REMOTE_PATH_INPUT, AppSettings.SERVER_REMOTE_PATH.defaultValue,
appSettings.serverRemotePath appSettings.serverRemotePath,
) )
// ADB Settings // ADB Settings
migrateString( migrateString(
AppPreferenceKeys.ADB_KEY_NAME, ADB_KEY_NAME,
AppDefaults.ADB_KEY_NAME_INPUT, AppSettings.ADB_KEY_NAME.defaultValue,
appSettings.adbKeyName appSettings.adbKeyName,
) )
migrateBoolean( migrateBoolean(
AppPreferenceKeys.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN, ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN,
AppDefaults.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN, AppSettings.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN.defaultValue,
appSettings.adbPairingAutoDiscoverOnDialogOpen appSettings.adbPairingAutoDiscoverOnDialogOpen,
) )
migrateBoolean( migrateBoolean(
AppPreferenceKeys.ADB_AUTO_RECONNECT_PAIRED_DEVICE, ADB_AUTO_RECONNECT_PAIRED_DEVICE,
AppDefaults.ADB_AUTO_RECONNECT_PAIRED_DEVICE, AppSettings.ADB_AUTO_RECONNECT_PAIRED_DEVICE.defaultValue,
appSettings.adbAutoReconnectPairedDevice appSettings.adbAutoReconnectPairedDevice,
) )
migrateBoolean( migrateBoolean(
AppPreferenceKeys.ADB_MDNS_LAN_DISCOVERY, ADB_MDNS_LAN_DISCOVERY,
AppDefaults.ADB_MDNS_LAN_DISCOVERY, AppSettings.ADB_MDNS_LAN_DISCOVERY.defaultValue,
appSettings.adbMdnsLanDiscovery appSettings.adbMdnsLanDiscovery,
) )
Log.d(TAG, "AppSettings migration completed") Log.d(TAG, "AppSettings migration completed")
@@ -167,114 +160,115 @@ class PreferenceMigration(private val appContext: Context) {
// Audio & Video Codecs // Audio & Video Codecs
migrateString( migrateString(
AppPreferenceKeys.AUDIO_CODEC, AUDIO_CODEC,
AppDefaults.AUDIO_CODEC, ScrcpyOptions.AUDIO_CODEC.defaultValue,
scrcpyOptions.audioCodec scrcpyOptions.audioCodec,
) )
migrateString( migrateString(
AppPreferenceKeys.VIDEO_CODEC, VIDEO_CODEC,
AppDefaults.VIDEO_CODEC, ScrcpyOptions.VIDEO_CODEC.defaultValue,
scrcpyOptions.videoCodec scrcpyOptions.videoCodec,
) )
// Bit Rates // Bit Rates
val audioBitRateKbps = sharedPrefs.getInt( val audioBitRateKbps = sharedPrefs.getInt(
AppPreferenceKeys.AUDIO_BIT_RATE_KBPS, AUDIO_BIT_RATE_KBPS,
AppDefaults.AUDIO_BIT_RATE_KBPS ScrcpyOptions.AUDIO_BIT_RATE.defaultValue / 1_000,
) )
scrcpyOptions.audioBitRate.set(audioBitRateKbps * 1000) // Convert to bps scrcpyOptions.audioBitRate.set(audioBitRateKbps * 1_000) // Convert to bps
val videoBitRateMbps = sharedPrefs.getFloat( val videoBitRateMbps = sharedPrefs.getFloat(
AppPreferenceKeys.VIDEO_BIT_RATE_MBPS, VIDEO_BIT_RATE_MBPS,
AppDefaults.VIDEO_BIT_RATE_MBPS (ScrcpyOptions.VIDEO_BIT_RATE.defaultValue / 1_000_000).toFloat(),
) )
scrcpyOptions.videoBitRate.set((videoBitRateMbps * 1_000_000).toInt()) scrcpyOptions.videoBitRate.set((videoBitRateMbps * 1_000_000).toInt())
// Control Options // Control Options
migrateBoolean( migrateBoolean(
AppPreferenceKeys.TURN_SCREEN_OFF, TURN_SCREEN_OFF,
AppDefaults.TURN_SCREEN_OFF, ScrcpyOptions.TURN_SCREEN_OFF.defaultValue,
scrcpyOptions.turnScreenOff scrcpyOptions.turnScreenOff,
) )
migrateBoolean( migrateBoolean(
AppPreferenceKeys.NO_CONTROL, NO_CONTROL,
AppDefaults.NO_CONTROL !ScrcpyOptions.CONTROL.defaultValue,
) { value -> ) { value ->
scrcpyOptions.control.set(!value) // Invert logic scrcpyOptions.control.set(!value) // Invert logic
} }
migrateBoolean( migrateBoolean(
AppPreferenceKeys.NO_VIDEO, NO_VIDEO,
AppDefaults.NO_VIDEO !ScrcpyOptions.VIDEO.defaultValue,
) { value -> ) { value ->
scrcpyOptions.video.set(!value) // Invert logic scrcpyOptions.video.set(!value) // Invert logic
} }
// Video Source // Video Source
val videoSourcePreset = sharedPrefs.getString( val videoSourcePreset = sharedPrefs.getString(
AppPreferenceKeys.VIDEO_SOURCE_PRESET, VIDEO_SOURCE_PRESET,
AppDefaults.VIDEO_SOURCE_PRESET ScrcpyOptions.VIDEO_SOURCE.defaultValue,
).orEmpty().ifBlank { AppDefaults.VIDEO_SOURCE_PRESET } ).orEmpty().ifBlank { ScrcpyOptions.VIDEO_SOURCE.defaultValue }
scrcpyOptions.videoSource.set(videoSourcePreset) scrcpyOptions.videoSource.set(videoSourcePreset)
migrateString( migrateString(
AppPreferenceKeys.DISPLAY_ID, DISPLAY_ID,
AppDefaults.DISPLAY_ID ScrcpyOptions.DISPLAY_ID.defaultValue.toString(),
) { value -> ) { value ->
value.toIntOrNull()?.let { scrcpyOptions.displayId.set(it) } value.toIntOrNull()?.let { scrcpyOptions.displayId.set(it) }
} }
// Camera Settings // Camera Settings
migrateString( migrateString(
AppPreferenceKeys.CAMERA_ID, CAMERA_ID,
AppDefaults.CAMERA_ID, ScrcpyOptions.CAMERA_ID.defaultValue,
scrcpyOptions.cameraId scrcpyOptions.cameraId,
) )
migrateString( migrateString(
AppPreferenceKeys.CAMERA_FACING_PRESET, CAMERA_FACING_PRESET,
AppDefaults.CAMERA_FACING_PRESET, ScrcpyOptions.CAMERA_FACING.defaultValue,
scrcpyOptions.cameraFacing scrcpyOptions.cameraFacing,
) )
migrateString( migrateString(
AppPreferenceKeys.CAMERA_SIZE_PRESET, CAMERA_SIZE_PRESET,
AppDefaults.CAMERA_SIZE_PRESET ScrcpyOptions.CAMERA_SIZE.defaultValue,
) { value -> ) { value ->
if (value == "custom") { if (value == "custom") {
val customSize = sharedPrefs.getString( val customSize = sharedPrefs.getString(
AppPreferenceKeys.CAMERA_SIZE_CUSTOM, CAMERA_SIZE_CUSTOM,
AppDefaults.CAMERA_SIZE_CUSTOM ScrcpyOptions.CAMERA_SIZE_CUSTOM.defaultValue,
).orEmpty() ).orEmpty()
scrcpyOptions.cameraSize.set(customSize) scrcpyOptions.cameraSizeCustom.set(customSize)
scrcpyOptions.cameraSizeUseCustom.set(true)
} else { } else {
scrcpyOptions.cameraSize.set(value) scrcpyOptions.cameraSize.set(value)
} }
} }
migrateString( migrateString(
AppPreferenceKeys.CAMERA_AR, CAMERA_AR,
AppDefaults.CAMERA_AR, ScrcpyOptions.CAMERA_AR.defaultValue,
scrcpyOptions.cameraAr scrcpyOptions.cameraAr,
) )
migrateString( migrateString(
AppPreferenceKeys.CAMERA_FPS, CAMERA_FPS,
AppDefaults.CAMERA_FPS ScrcpyOptions.CAMERA_FPS.defaultValue.toString(),
) { value -> ) { value ->
value.toIntOrNull()?.let { scrcpyOptions.cameraFps.set(it) } value.toIntOrNull()?.let { scrcpyOptions.cameraFps.set(it) }
} }
migrateBoolean( migrateBoolean(
AppPreferenceKeys.CAMERA_HIGH_SPEED, CAMERA_HIGH_SPEED,
AppDefaults.CAMERA_HIGH_SPEED, ScrcpyOptions.CAMERA_HIGH_SPEED.defaultValue,
scrcpyOptions.cameraHighSpeed scrcpyOptions.cameraHighSpeed,
) )
// Audio Source // Audio Source
val audioSourcePreset = sharedPrefs.getString( val audioSourcePreset = sharedPrefs.getString(
AppPreferenceKeys.AUDIO_SOURCE_PRESET, AUDIO_SOURCE_PRESET,
AppDefaults.AUDIO_SOURCE_PRESET "auto",
).orEmpty().ifBlank { AppDefaults.AUDIO_SOURCE_PRESET } ).orEmpty().ifBlank { "auto" }
if (audioSourcePreset == "custom") { if (audioSourcePreset == "custom") {
val customSource = sharedPrefs.getString( val customSource = sharedPrefs.getString(
AppPreferenceKeys.AUDIO_SOURCE_CUSTOM, AUDIO_SOURCE_CUSTOM,
AppDefaults.AUDIO_SOURCE_CUSTOM ScrcpyOptions.AUDIO_SOURCE.defaultValue,
).orEmpty() ).orEmpty()
scrcpyOptions.audioSource.set(customSource) scrcpyOptions.audioSource.set(customSource)
} else { } else {
@@ -282,69 +276,69 @@ class PreferenceMigration(private val appContext: Context) {
} }
migrateBoolean( migrateBoolean(
AppPreferenceKeys.AUDIO_DUP, AUDIO_DUP,
AppDefaults.AUDIO_DUP, ScrcpyOptions.AUDIO_DUP.defaultValue,
scrcpyOptions.audioDup scrcpyOptions.audioDup,
) )
migrateBoolean( migrateBoolean(
AppPreferenceKeys.NO_AUDIO_PLAYBACK, NO_AUDIO_PLAYBACK,
AppDefaults.NO_AUDIO_PLAYBACK !ScrcpyOptions.AUDIO_PLAYBACK.defaultValue,
) { value -> ) { value ->
scrcpyOptions.audioPlayback.set(!value) // Invert logic scrcpyOptions.audioPlayback.set(!value) // Invert logic
} }
migrateBoolean( migrateBoolean(
AppPreferenceKeys.REQUIRE_AUDIO, REQUIRE_AUDIO,
AppDefaults.REQUIRE_AUDIO, ScrcpyOptions.REQUIRE_AUDIO.defaultValue,
scrcpyOptions.requireAudio scrcpyOptions.requireAudio,
) )
// Max Size & FPS // Max Size & FPS
migrateString( migrateString(
AppPreferenceKeys.MAX_SIZE_INPUT, MAX_SIZE_INPUT,
AppDefaults.MAX_SIZE_INPUT ScrcpyOptions.MAX_SIZE.defaultValue.toString(),
) { value -> ) { value ->
value.toIntOrNull()?.let { scrcpyOptions.maxSize.set(it) } value.toIntOrNull()?.let { scrcpyOptions.maxSize.set(it) }
} }
migrateString( migrateString(
AppPreferenceKeys.MAX_FPS_INPUT, MAX_FPS_INPUT,
AppDefaults.MAX_FPS_INPUT, ScrcpyOptions.MAX_FPS.defaultValue,
scrcpyOptions.maxFps scrcpyOptions.maxFps,
) )
// Encoders & Codec Options // Encoders & Codec Options
migrateString( migrateString(
AppPreferenceKeys.VIDEO_ENCODER, VIDEO_ENCODER,
AppDefaults.VIDEO_ENCODER, ScrcpyOptions.VIDEO_ENCODER.defaultValue,
scrcpyOptions.videoEncoder scrcpyOptions.videoEncoder,
) )
migrateString( migrateString(
AppPreferenceKeys.VIDEO_CODEC_OPTION, VIDEO_CODEC_OPTION,
AppDefaults.VIDEO_CODEC_OPTION, ScrcpyOptions.VIDEO_CODEC_OPTIONS.defaultValue,
scrcpyOptions.videoCodecOptions scrcpyOptions.videoCodecOptions,
) )
migrateString( migrateString(
AppPreferenceKeys.AUDIO_ENCODER, AUDIO_ENCODER,
AppDefaults.AUDIO_ENCODER, ScrcpyOptions.AUDIO_ENCODER.defaultValue,
scrcpyOptions.audioEncoder scrcpyOptions.audioEncoder,
) )
migrateString( migrateString(
AppPreferenceKeys.AUDIO_CODEC_OPTION, AUDIO_CODEC_OPTION,
AppDefaults.AUDIO_CODEC_OPTION, ScrcpyOptions.AUDIO_CODEC_OPTIONS.defaultValue,
scrcpyOptions.audioCodecOptions scrcpyOptions.audioCodecOptions,
) )
// New Display // New Display
val newDisplayWidth = sharedPrefs.getString( val newDisplayWidth = sharedPrefs.getString(
AppPreferenceKeys.NEW_DISPLAY_WIDTH, NEW_DISPLAY_WIDTH,
AppDefaults.NEW_DISPLAY_WIDTH "",
).orEmpty() ).orEmpty()
val newDisplayHeight = sharedPrefs.getString( val newDisplayHeight = sharedPrefs.getString(
AppPreferenceKeys.NEW_DISPLAY_HEIGHT, NEW_DISPLAY_HEIGHT,
AppDefaults.NEW_DISPLAY_HEIGHT "",
).orEmpty() ).orEmpty()
val newDisplayDpi = sharedPrefs.getString( val newDisplayDpi = sharedPrefs.getString(
AppPreferenceKeys.NEW_DISPLAY_DPI, NEW_DISPLAY_DPI,
AppDefaults.NEW_DISPLAY_DPI "",
).orEmpty() ).orEmpty()
if (newDisplayWidth.isNotBlank() && newDisplayHeight.isNotBlank()) { if (newDisplayWidth.isNotBlank() && newDisplayHeight.isNotBlank()) {
@@ -358,34 +352,33 @@ class PreferenceMigration(private val appContext: Context) {
// Crop // Crop
val cropWidth = sharedPrefs.getString( val cropWidth = sharedPrefs.getString(
AppPreferenceKeys.CROP_WIDTH, CROP_WIDTH,
AppDefaults.CROP_WIDTH "",
).orEmpty() ).orEmpty()
val cropHeight = sharedPrefs.getString( val cropHeight = sharedPrefs.getString(
AppPreferenceKeys.CROP_HEIGHT, CROP_HEIGHT,
AppDefaults.CROP_HEIGHT "",
).orEmpty() ).orEmpty()
val cropX = sharedPrefs.getString( val cropX = sharedPrefs.getString(
AppPreferenceKeys.CROP_X, CROP_X,
AppDefaults.CROP_X "",
).orEmpty() ).orEmpty()
val cropY = sharedPrefs.getString( val cropY = sharedPrefs.getString(
AppPreferenceKeys.CROP_Y, CROP_Y,
AppDefaults.CROP_Y "",
).orEmpty() ).orEmpty()
if (cropWidth.isNotBlank() && cropHeight.isNotBlank() && if (cropWidth.isNotBlank() && cropHeight.isNotBlank()
cropX.isNotBlank() && cropY.isNotBlank() && cropX.isNotBlank() && cropY.isNotBlank()
) { ) {
scrcpyOptions.crop.set("${cropWidth}:${cropHeight}:${cropX}:${cropY}") scrcpyOptions.crop.set("${cropWidth}:${cropHeight}:${cropX}:${cropY}")
} }
// Audio enabled flag (convert to audio option) migrateBoolean(
val audioEnabled = sharedPrefs.getBoolean( AUDIO_ENABLED,
AppPreferenceKeys.AUDIO_ENABLED, ScrcpyOptions.AUDIO.defaultValue,
AppDefaults.AUDIO_ENABLED scrcpyOptions.audio,
) )
scrcpyOptions.audio.set(audioEnabled)
Log.d(TAG, "ScrcpyOptions migration completed") Log.d(TAG, "ScrcpyOptions migration completed")
} }
@@ -398,7 +391,7 @@ class PreferenceMigration(private val appContext: Context) {
// Migrate quick devices list // Migrate quick devices list
val quickDevicesRaw = appSharedPrefs.getString( val quickDevicesRaw = appSharedPrefs.getString(
AppPreferenceKeys.QUICK_DEVICES, QUICK_DEVICES,
"" ""
).orEmpty() ).orEmpty()
if (quickDevicesRaw.isNotBlank()) { if (quickDevicesRaw.isNotBlank()) {
@@ -407,27 +400,27 @@ class PreferenceMigration(private val appContext: Context) {
// Migrate quick connect input // Migrate quick connect input
migrateString( migrateString(
AppPreferenceKeys.QUICK_CONNECT_INPUT, QUICK_CONNECT_INPUT,
AppDefaults.QUICK_CONNECT_INPUT, QuickDevices.QUICK_CONNECT_INPUT.defaultValue,
quickDevices.quickConnectInput quickDevices.quickConnectInput,
) )
Log.d(TAG, "QuickDevices migration completed") Log.d(TAG, "QuickDevices migration completed")
} }
/** /**
* 迁移 ADB 客户端数据RSA 密钥) * 迁移 ADB 客户端数据RSA 密钥)
*/ */
private suspend fun migrateAdbClientData() { private suspend fun migrateAdbClientData() {
val adbClientData = Storage.adbClientData val adbClientData = Storage.adbClientData
// 迁移 RSA 私钥 // 迁移 RSA 私钥
val privKey = sharedPrefs.getString("priv", null) val privKey = sharedPrefs.getString("priv", null)
if (privKey != null) { if (privKey != null) {
adbClientData.rsaPrivateKey.set(privKey) adbClientData.rsaPrivateKey.set(privKey)
Log.d(TAG, "ADB RSA private key migrated") Log.d(TAG, "ADB RSA private key migrated")
} }
Log.d(TAG, "AdbClientData migration completed") Log.d(TAG, "AdbClientData migration completed")
} }
@@ -464,6 +457,15 @@ class PreferenceMigration(private val appContext: Context) {
settingProperty.set(value) settingProperty.set(value)
} }
private suspend fun migrateInt(
key: String,
defaultValue: Int,
action: suspend (Int) -> Unit
) {
val value = appSharedPrefs.getInt(key, defaultValue)
action(value)
}
private suspend fun migrateBoolean( private suspend fun migrateBoolean(
key: String, key: String,
defaultValue: Boolean, defaultValue: Boolean,
@@ -481,4 +483,82 @@ class PreferenceMigration(private val appContext: Context) {
val value = appSharedPrefs.getBoolean(key, defaultValue) val value = appSharedPrefs.getBoolean(key, defaultValue)
action(value) action(value)
} }
companion object {
const val PREFS_NAME = "scrcpy_app_prefs"
// Devices
const val QUICK_DEVICES = "quick_devices"
const val QUICK_CONNECT_INPUT = "quick_connect_input"
const val PAIR_HOST = "pair_host"
const val PAIR_PORT = "pair_port"
const val PAIR_CODE = "pair_code"
const val AUDIO_ENABLED = "audio_enabled"
const val AUDIO_CODEC = "audio_codec"
const val AUDIO_BIT_RATE_INPUT = "audio_bit_rate_input"
const val AUDIO_BIT_RATE_KBPS = "audio_bit_rate_kbps"
const val VIDEO_CODEC = "video_codec"
const val VIDEO_BIT_RATE_MBPS = "video_bit_rate_mbps"
const val VIDEO_BIT_RATE_INPUT = "video_bit_rate_input"
const val TURN_SCREEN_OFF = "turn_screen_off"
const val NO_CONTROL = "no_control"
const val NO_VIDEO = "no_video"
const val VIDEO_SOURCE_PRESET = "video_source_preset"
const val DISPLAY_ID = "display_id"
const val CAMERA_ID = "camera_id"
const val CAMERA_FACING_PRESET = "camera_facing_preset"
const val CAMERA_SIZE_PRESET = "camera_size_preset"
const val CAMERA_SIZE_CUSTOM = "camera_size_custom"
const val CAMERA_AR = "camera_ar"
const val CAMERA_FPS = "camera_fps"
const val CAMERA_HIGH_SPEED = "camera_high_speed"
const val AUDIO_SOURCE_PRESET = "audio_source_preset"
const val AUDIO_SOURCE_CUSTOM = "audio_source_custom"
const val AUDIO_DUP = "audio_dup"
const val NO_AUDIO_PLAYBACK = "no_audio_playback"
const val REQUIRE_AUDIO = "require_audio"
const val MAX_SIZE_INPUT = "max_size_input"
const val MAX_FPS_INPUT = "max_fps_input"
const val VIDEO_ENCODER = "video_encoder"
const val VIDEO_CODEC_OPTION = "video_codec_options"
const val AUDIO_ENCODER = "audio_encoder"
const val AUDIO_CODEC_OPTION = "audio_codec_options"
const val NEW_DISPLAY_WIDTH = "new_display_width"
const val NEW_DISPLAY_HEIGHT = "new_display_height"
const val NEW_DISPLAY_DPI = "new_display_dpi"
const val CROP_WIDTH = "crop_width"
const val CROP_HEIGHT = "crop_height"
const val CROP_X = "crop_x"
const val CROP_Y = "crop_y"
// Settings
const val THEME_BASE_INDEX = "theme_base_index"
const val MONET = "monet"
const val FULLSCREEN_DEBUG_INFO = "fullscreen_debug_info"
const val SHOW_FULLSCREEN_VIRTUAL_BUTTONS = "show_fullscreen_virtual_buttons"
const val DEVICE_PREVIEW_CARD_HEIGHT_DP = "device_preview_card_height_dp"
const val PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT = "preview_virtual_button_show_text"
const val VIRTUAL_BUTTONS_LAYOUT = "virtual_buttons_layout"
const val CUSTOM_SERVER_URI = "custom_server_uri"
const val SERVER_REMOTE_PATH = "server_remote_path"
const val ADB_KEY_NAME = "adb_key_name"
const val ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN =
"adb_pairing_auto_discover_on_dialog_open"
const val ADB_AUTO_RECONNECT_PAIRED_DEVICE = "adb_auto_reconnect_paired_device"
const val ADB_MDNS_LAN_DISCOVERY = "adb_mdns_lan_discovery"
}
} }