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

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
object Defaults {
const val EVENT_LOG_LINES = 512
const val ADB_PORT = 5555;
}

View File

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

View File

@@ -5,7 +5,6 @@ import android.content.Intent
import android.content.pm.ActivityInfo
import android.net.Uri
import android.os.SystemClock
import android.view.WindowManager
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.activity.compose.PredictiveBackHandler
@@ -266,27 +265,11 @@ fun MainScreen() {
}
}
var sessionStarted by remember { mutableStateOf(false) }
var showReorderDevices by rememberSaveable { mutableStateOf(false) }
var fullscreenOrientation by rememberSaveable {
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.
LaunchedEffect(activity, currentRootScreen, fullscreenOrientation) {
val targetOrientation = when (currentRootScreen) {
@@ -356,7 +339,6 @@ fun MainScreen() {
snackbar = snackbarController,
scrollBehavior = devicesPageScrollBehavior,
onOpenVirtualButtonOrder = { rootBackStack.add(RootScreen.VirtualButtonOrder) },
onSessionStartedChange = { sessionStarted = it },
onOpenReorderDevices = { showReorderDevices = true },
onOpenAdvancedPage = { rootBackStack.add(RootScreen.Advanced) },
onOpenFullscreenPage = { session ->

View File

@@ -264,14 +264,19 @@ internal fun ScrcpyAllOptionsPage(
val videoEncoderDropdownItems = rememberSaveable(videoEncoders, listRefreshVersion) {
listOf("") + videoEncoders
}
val videoEncoderIndex = rememberSaveable(soBundle.videoEncoder, videoEncoders, listRefreshVersion) {
(videoEncoders.indexOf(soBundle.videoEncoder) + 1).coerceAtLeast(0)
}
val videoEncoderIndex =
rememberSaveable(soBundle.videoEncoder, videoEncoders, listRefreshVersion) {
(videoEncoders.indexOf(soBundle.videoEncoder) + 1).coerceAtLeast(0)
}
val audioEncoderDropdownItems = rememberSaveable(audioEncoders, listRefreshVersion) {
listOf("") + audioEncoders
}
val audioEncoderIndex = rememberSaveable(soBundle.audioEncoder, audioEncoders, listRefreshVersion) {
val audioEncoderIndex = rememberSaveable(
soBundle.audioEncoder,
audioEncoders,
listRefreshVersion
) {
(audioEncoders.indexOf(soBundle.audioEncoder) + 1).coerceAtLeast(0)
}
@@ -419,6 +424,77 @@ internal fun ScrcpyAllOptionsPage(
},
// 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)
},
)
SuperSwitch(
title = "投屏时保持屏幕常亮",
summary = "Scrcpy 启动后保持本机屏幕常亮,避免锁屏导致 ADB 断开",
checked = asBundle.keepScreenOnWhenStreaming,
onCheckedChange = {
asBundle = asBundle.copy(keepScreenOnWhenStreaming = it)
},
)
SuperSlider(
title = "预览卡高度",
summary = "设备页预览卡高度",

View File

@@ -32,18 +32,16 @@ fun LazyColumn(
content: LazyListScope.() -> Unit,
) {
val focusManager = LocalFocusManager.current
val focusClearModifier = if (clearFocusOnTap) {
Modifier.pointerInput(Unit) {
detectTapGestures(onTap = { focusManager.clearFocus() })
}
} else {
Modifier
}
LazyColumn(
modifier = modifier
.fillMaxSize()
.then(focusClearModifier)
.then(
if (clearFocusOnTap)
Modifier.pointerInput(Unit) {
detectTapGestures(onTap = { focusManager.clearFocus() })
} else Modifier
)
.overScrollVertical()
.nestedScroll(scrollBehavior.nestedScrollConnection)
.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.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -12,6 +13,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
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 top.yukonga.miuix.kmp.basic.ButtonDefaults
import top.yukonga.miuix.kmp.basic.Slider
@@ -131,6 +134,10 @@ private fun SliderInputDialog(
label = label,
useLabelAsPlaceholder = useLabelAsPlaceholder,
maxLines = 1,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done,
),
onValueChange = { 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 {
private const val LOG_TAG = "EventLogger"
const val MAX_LINES = 512
private val _eventLog: SnapshotStateList<String> = mutableStateListOf()
/**
@@ -36,8 +38,8 @@ object EventLogger {
_eventLog.add(0, "[$timestamp] $message")
// Rotate logs if exceeds max size
if (_eventLog.size > Defaults.EVENT_LOG_LINES) {
_eventLog.removeRange(Defaults.EVENT_LOG_LINES, _eventLog.size)
if (_eventLog.size > MAX_LINES) {
_eventLog.removeRange(MAX_LINES, _eventLog.size)
}
// Log to Android logcat

View File

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

View File

@@ -4,8 +4,6 @@ import android.content.Context
import android.content.SharedPreferences
import android.util.Log
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.withContext
@@ -16,7 +14,7 @@ private const val TAG = "PreferenceMigration"
*/
class PreferenceMigration(private val appContext: Context) {
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 {
@@ -80,80 +78,75 @@ class PreferenceMigration(private val appContext: Context) {
// Theme Settings
migrateInt(
AppPreferenceKeys.THEME_BASE_INDEX,
AppDefaults.THEME_BASE_INDEX,
appSettings.themeBaseIndex
THEME_BASE_INDEX,
AppSettings.THEME_BASE_INDEX.defaultValue,
appSettings.themeBaseIndex,
)
migrateBoolean(
AppPreferenceKeys.MONET,
AppDefaults.MONET,
appSettings.monet
MONET,
AppSettings.MONET.defaultValue,
appSettings.monet,
)
// Scrcpy Settings
migrateBoolean(
AppPreferenceKeys.FULLSCREEN_DEBUG_INFO,
AppDefaults.FULLSCREEN_DEBUG_INFO,
appSettings.fullscreenDebugInfo
FULLSCREEN_DEBUG_INFO,
AppSettings.FULLSCREEN_DEBUG_INFO.defaultValue,
appSettings.fullscreenDebugInfo,
)
migrateBoolean(
AppPreferenceKeys.SHOW_FULLSCREEN_VIRTUAL_BUTTONS,
AppDefaults.SHOW_FULLSCREEN_VIRTUAL_BUTTONS,
appSettings.showFullscreenVirtualButtons
)
migrateBoolean(
AppPreferenceKeys.KEEP_SCREEN_ON_WHEN_STREAMING,
AppDefaults.KEEP_SCREEN_ON_WHEN_STREAMING,
appSettings.keepScreenOnWhenStreaming
SHOW_FULLSCREEN_VIRTUAL_BUTTONS,
AppSettings.SHOW_FULLSCREEN_VIRTUAL_BUTTONS.defaultValue,
appSettings.showFullscreenVirtualButtons,
)
migrateInt(
AppPreferenceKeys.DEVICE_PREVIEW_CARD_HEIGHT_DP,
AppDefaults.DEVICE_PREVIEW_CARD_HEIGHT_DP,
appSettings.devicePreviewCardHeightDp
DEVICE_PREVIEW_CARD_HEIGHT_DP,
AppSettings.DEVICE_PREVIEW_CARD_HEIGHT_DP.defaultValue,
appSettings.devicePreviewCardHeightDp,
)
migrateBoolean(
AppPreferenceKeys.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT,
AppDefaults.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT,
appSettings.previewVirtualButtonShowText
PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT,
AppSettings.PREVIEW_VIRTUAL_BUTTON_SHOW_TEXT.defaultValue,
appSettings.previewVirtualButtonShowText,
)
migrateString(
AppPreferenceKeys.VIRTUAL_BUTTONS_LAYOUT,
AppDefaults.VIRTUAL_BUTTONS_LAYOUT,
appSettings.virtualButtonsLayout
VIRTUAL_BUTTONS_LAYOUT,
AppSettings.VIRTUAL_BUTTONS_LAYOUT.defaultValue,
appSettings.virtualButtonsLayout,
)
// Scrcpy Server Settings
migrateString(
AppPreferenceKeys.CUSTOM_SERVER_URI,
AppDefaults.CUSTOM_SERVER_URI ?: "",
appSettings.customServerUri
CUSTOM_SERVER_URI,
AppSettings.CUSTOM_SERVER_URI.defaultValue,
appSettings.customServerUri,
)
migrateString(
AppPreferenceKeys.SERVER_REMOTE_PATH,
AppDefaults.SERVER_REMOTE_PATH_INPUT,
appSettings.serverRemotePath
SERVER_REMOTE_PATH,
AppSettings.SERVER_REMOTE_PATH.defaultValue,
appSettings.serverRemotePath,
)
// ADB Settings
migrateString(
AppPreferenceKeys.ADB_KEY_NAME,
AppDefaults.ADB_KEY_NAME_INPUT,
appSettings.adbKeyName
ADB_KEY_NAME,
AppSettings.ADB_KEY_NAME.defaultValue,
appSettings.adbKeyName,
)
migrateBoolean(
AppPreferenceKeys.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN,
AppDefaults.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN,
appSettings.adbPairingAutoDiscoverOnDialogOpen
ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN,
AppSettings.ADB_PAIRING_AUTO_DISCOVER_ON_DIALOG_OPEN.defaultValue,
appSettings.adbPairingAutoDiscoverOnDialogOpen,
)
migrateBoolean(
AppPreferenceKeys.ADB_AUTO_RECONNECT_PAIRED_DEVICE,
AppDefaults.ADB_AUTO_RECONNECT_PAIRED_DEVICE,
appSettings.adbAutoReconnectPairedDevice
ADB_AUTO_RECONNECT_PAIRED_DEVICE,
AppSettings.ADB_AUTO_RECONNECT_PAIRED_DEVICE.defaultValue,
appSettings.adbAutoReconnectPairedDevice,
)
migrateBoolean(
AppPreferenceKeys.ADB_MDNS_LAN_DISCOVERY,
AppDefaults.ADB_MDNS_LAN_DISCOVERY,
appSettings.adbMdnsLanDiscovery
ADB_MDNS_LAN_DISCOVERY,
AppSettings.ADB_MDNS_LAN_DISCOVERY.defaultValue,
appSettings.adbMdnsLanDiscovery,
)
Log.d(TAG, "AppSettings migration completed")
@@ -167,114 +160,115 @@ class PreferenceMigration(private val appContext: Context) {
// Audio & Video Codecs
migrateString(
AppPreferenceKeys.AUDIO_CODEC,
AppDefaults.AUDIO_CODEC,
scrcpyOptions.audioCodec
AUDIO_CODEC,
ScrcpyOptions.AUDIO_CODEC.defaultValue,
scrcpyOptions.audioCodec,
)
migrateString(
AppPreferenceKeys.VIDEO_CODEC,
AppDefaults.VIDEO_CODEC,
scrcpyOptions.videoCodec
VIDEO_CODEC,
ScrcpyOptions.VIDEO_CODEC.defaultValue,
scrcpyOptions.videoCodec,
)
// Bit Rates
val audioBitRateKbps = sharedPrefs.getInt(
AppPreferenceKeys.AUDIO_BIT_RATE_KBPS,
AppDefaults.AUDIO_BIT_RATE_KBPS
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(
AppPreferenceKeys.VIDEO_BIT_RATE_MBPS,
AppDefaults.VIDEO_BIT_RATE_MBPS
VIDEO_BIT_RATE_MBPS,
(ScrcpyOptions.VIDEO_BIT_RATE.defaultValue / 1_000_000).toFloat(),
)
scrcpyOptions.videoBitRate.set((videoBitRateMbps * 1_000_000).toInt())
// Control Options
migrateBoolean(
AppPreferenceKeys.TURN_SCREEN_OFF,
AppDefaults.TURN_SCREEN_OFF,
scrcpyOptions.turnScreenOff
TURN_SCREEN_OFF,
ScrcpyOptions.TURN_SCREEN_OFF.defaultValue,
scrcpyOptions.turnScreenOff,
)
migrateBoolean(
AppPreferenceKeys.NO_CONTROL,
AppDefaults.NO_CONTROL
NO_CONTROL,
!ScrcpyOptions.CONTROL.defaultValue,
) { value ->
scrcpyOptions.control.set(!value) // Invert logic
}
migrateBoolean(
AppPreferenceKeys.NO_VIDEO,
AppDefaults.NO_VIDEO
NO_VIDEO,
!ScrcpyOptions.VIDEO.defaultValue,
) { value ->
scrcpyOptions.video.set(!value) // Invert logic
}
// Video Source
val videoSourcePreset = sharedPrefs.getString(
AppPreferenceKeys.VIDEO_SOURCE_PRESET,
AppDefaults.VIDEO_SOURCE_PRESET
).orEmpty().ifBlank { AppDefaults.VIDEO_SOURCE_PRESET }
VIDEO_SOURCE_PRESET,
ScrcpyOptions.VIDEO_SOURCE.defaultValue,
).orEmpty().ifBlank { ScrcpyOptions.VIDEO_SOURCE.defaultValue }
scrcpyOptions.videoSource.set(videoSourcePreset)
migrateString(
AppPreferenceKeys.DISPLAY_ID,
AppDefaults.DISPLAY_ID
DISPLAY_ID,
ScrcpyOptions.DISPLAY_ID.defaultValue.toString(),
) { value ->
value.toIntOrNull()?.let { scrcpyOptions.displayId.set(it) }
}
// Camera Settings
migrateString(
AppPreferenceKeys.CAMERA_ID,
AppDefaults.CAMERA_ID,
scrcpyOptions.cameraId
CAMERA_ID,
ScrcpyOptions.CAMERA_ID.defaultValue,
scrcpyOptions.cameraId,
)
migrateString(
AppPreferenceKeys.CAMERA_FACING_PRESET,
AppDefaults.CAMERA_FACING_PRESET,
scrcpyOptions.cameraFacing
CAMERA_FACING_PRESET,
ScrcpyOptions.CAMERA_FACING.defaultValue,
scrcpyOptions.cameraFacing,
)
migrateString(
AppPreferenceKeys.CAMERA_SIZE_PRESET,
AppDefaults.CAMERA_SIZE_PRESET
CAMERA_SIZE_PRESET,
ScrcpyOptions.CAMERA_SIZE.defaultValue,
) { value ->
if (value == "custom") {
val customSize = sharedPrefs.getString(
AppPreferenceKeys.CAMERA_SIZE_CUSTOM,
AppDefaults.CAMERA_SIZE_CUSTOM
CAMERA_SIZE_CUSTOM,
ScrcpyOptions.CAMERA_SIZE_CUSTOM.defaultValue,
).orEmpty()
scrcpyOptions.cameraSize.set(customSize)
scrcpyOptions.cameraSizeCustom.set(customSize)
scrcpyOptions.cameraSizeUseCustom.set(true)
} else {
scrcpyOptions.cameraSize.set(value)
}
}
migrateString(
AppPreferenceKeys.CAMERA_AR,
AppDefaults.CAMERA_AR,
scrcpyOptions.cameraAr
CAMERA_AR,
ScrcpyOptions.CAMERA_AR.defaultValue,
scrcpyOptions.cameraAr,
)
migrateString(
AppPreferenceKeys.CAMERA_FPS,
AppDefaults.CAMERA_FPS
CAMERA_FPS,
ScrcpyOptions.CAMERA_FPS.defaultValue.toString(),
) { value ->
value.toIntOrNull()?.let { scrcpyOptions.cameraFps.set(it) }
}
migrateBoolean(
AppPreferenceKeys.CAMERA_HIGH_SPEED,
AppDefaults.CAMERA_HIGH_SPEED,
scrcpyOptions.cameraHighSpeed
CAMERA_HIGH_SPEED,
ScrcpyOptions.CAMERA_HIGH_SPEED.defaultValue,
scrcpyOptions.cameraHighSpeed,
)
// Audio Source
val audioSourcePreset = sharedPrefs.getString(
AppPreferenceKeys.AUDIO_SOURCE_PRESET,
AppDefaults.AUDIO_SOURCE_PRESET
).orEmpty().ifBlank { AppDefaults.AUDIO_SOURCE_PRESET }
AUDIO_SOURCE_PRESET,
"auto",
).orEmpty().ifBlank { "auto" }
if (audioSourcePreset == "custom") {
val customSource = sharedPrefs.getString(
AppPreferenceKeys.AUDIO_SOURCE_CUSTOM,
AppDefaults.AUDIO_SOURCE_CUSTOM
AUDIO_SOURCE_CUSTOM,
ScrcpyOptions.AUDIO_SOURCE.defaultValue,
).orEmpty()
scrcpyOptions.audioSource.set(customSource)
} else {
@@ -282,69 +276,69 @@ class PreferenceMigration(private val appContext: Context) {
}
migrateBoolean(
AppPreferenceKeys.AUDIO_DUP,
AppDefaults.AUDIO_DUP,
scrcpyOptions.audioDup
AUDIO_DUP,
ScrcpyOptions.AUDIO_DUP.defaultValue,
scrcpyOptions.audioDup,
)
migrateBoolean(
AppPreferenceKeys.NO_AUDIO_PLAYBACK,
AppDefaults.NO_AUDIO_PLAYBACK
NO_AUDIO_PLAYBACK,
!ScrcpyOptions.AUDIO_PLAYBACK.defaultValue,
) { value ->
scrcpyOptions.audioPlayback.set(!value) // Invert logic
}
migrateBoolean(
AppPreferenceKeys.REQUIRE_AUDIO,
AppDefaults.REQUIRE_AUDIO,
scrcpyOptions.requireAudio
REQUIRE_AUDIO,
ScrcpyOptions.REQUIRE_AUDIO.defaultValue,
scrcpyOptions.requireAudio,
)
// Max Size & FPS
migrateString(
AppPreferenceKeys.MAX_SIZE_INPUT,
AppDefaults.MAX_SIZE_INPUT
MAX_SIZE_INPUT,
ScrcpyOptions.MAX_SIZE.defaultValue.toString(),
) { value ->
value.toIntOrNull()?.let { scrcpyOptions.maxSize.set(it) }
}
migrateString(
AppPreferenceKeys.MAX_FPS_INPUT,
AppDefaults.MAX_FPS_INPUT,
scrcpyOptions.maxFps
MAX_FPS_INPUT,
ScrcpyOptions.MAX_FPS.defaultValue,
scrcpyOptions.maxFps,
)
// Encoders & Codec Options
migrateString(
AppPreferenceKeys.VIDEO_ENCODER,
AppDefaults.VIDEO_ENCODER,
scrcpyOptions.videoEncoder
VIDEO_ENCODER,
ScrcpyOptions.VIDEO_ENCODER.defaultValue,
scrcpyOptions.videoEncoder,
)
migrateString(
AppPreferenceKeys.VIDEO_CODEC_OPTION,
AppDefaults.VIDEO_CODEC_OPTION,
scrcpyOptions.videoCodecOptions
VIDEO_CODEC_OPTION,
ScrcpyOptions.VIDEO_CODEC_OPTIONS.defaultValue,
scrcpyOptions.videoCodecOptions,
)
migrateString(
AppPreferenceKeys.AUDIO_ENCODER,
AppDefaults.AUDIO_ENCODER,
scrcpyOptions.audioEncoder
AUDIO_ENCODER,
ScrcpyOptions.AUDIO_ENCODER.defaultValue,
scrcpyOptions.audioEncoder,
)
migrateString(
AppPreferenceKeys.AUDIO_CODEC_OPTION,
AppDefaults.AUDIO_CODEC_OPTION,
scrcpyOptions.audioCodecOptions
AUDIO_CODEC_OPTION,
ScrcpyOptions.AUDIO_CODEC_OPTIONS.defaultValue,
scrcpyOptions.audioCodecOptions,
)
// New Display
val newDisplayWidth = sharedPrefs.getString(
AppPreferenceKeys.NEW_DISPLAY_WIDTH,
AppDefaults.NEW_DISPLAY_WIDTH
NEW_DISPLAY_WIDTH,
"",
).orEmpty()
val newDisplayHeight = sharedPrefs.getString(
AppPreferenceKeys.NEW_DISPLAY_HEIGHT,
AppDefaults.NEW_DISPLAY_HEIGHT
NEW_DISPLAY_HEIGHT,
"",
).orEmpty()
val newDisplayDpi = sharedPrefs.getString(
AppPreferenceKeys.NEW_DISPLAY_DPI,
AppDefaults.NEW_DISPLAY_DPI
NEW_DISPLAY_DPI,
"",
).orEmpty()
if (newDisplayWidth.isNotBlank() && newDisplayHeight.isNotBlank()) {
@@ -358,34 +352,33 @@ class PreferenceMigration(private val appContext: Context) {
// Crop
val cropWidth = sharedPrefs.getString(
AppPreferenceKeys.CROP_WIDTH,
AppDefaults.CROP_WIDTH
CROP_WIDTH,
"",
).orEmpty()
val cropHeight = sharedPrefs.getString(
AppPreferenceKeys.CROP_HEIGHT,
AppDefaults.CROP_HEIGHT
CROP_HEIGHT,
"",
).orEmpty()
val cropX = sharedPrefs.getString(
AppPreferenceKeys.CROP_X,
AppDefaults.CROP_X
CROP_X,
"",
).orEmpty()
val cropY = sharedPrefs.getString(
AppPreferenceKeys.CROP_Y,
AppDefaults.CROP_Y
CROP_Y,
"",
).orEmpty()
if (cropWidth.isNotBlank() && cropHeight.isNotBlank() &&
cropX.isNotBlank() && cropY.isNotBlank()
if (cropWidth.isNotBlank() && cropHeight.isNotBlank()
&& cropX.isNotBlank() && cropY.isNotBlank()
) {
scrcpyOptions.crop.set("${cropWidth}:${cropHeight}:${cropX}:${cropY}")
}
// Audio enabled flag (convert to audio option)
val audioEnabled = sharedPrefs.getBoolean(
AppPreferenceKeys.AUDIO_ENABLED,
AppDefaults.AUDIO_ENABLED
migrateBoolean(
AUDIO_ENABLED,
ScrcpyOptions.AUDIO.defaultValue,
scrcpyOptions.audio,
)
scrcpyOptions.audio.set(audioEnabled)
Log.d(TAG, "ScrcpyOptions migration completed")
}
@@ -398,7 +391,7 @@ class PreferenceMigration(private val appContext: Context) {
// Migrate quick devices list
val quickDevicesRaw = appSharedPrefs.getString(
AppPreferenceKeys.QUICK_DEVICES,
QUICK_DEVICES,
""
).orEmpty()
if (quickDevicesRaw.isNotBlank()) {
@@ -407,27 +400,27 @@ class PreferenceMigration(private val appContext: Context) {
// Migrate quick connect input
migrateString(
AppPreferenceKeys.QUICK_CONNECT_INPUT,
AppDefaults.QUICK_CONNECT_INPUT,
quickDevices.quickConnectInput
QUICK_CONNECT_INPUT,
QuickDevices.QUICK_CONNECT_INPUT.defaultValue,
quickDevices.quickConnectInput,
)
Log.d(TAG, "QuickDevices migration completed")
}
/**
* 迁移 ADB 客户端数据RSA 密钥)
*/
private suspend fun migrateAdbClientData() {
val adbClientData = Storage.adbClientData
// 迁移 RSA 私钥
val privKey = sharedPrefs.getString("priv", null)
if (privKey != null) {
adbClientData.rsaPrivateKey.set(privKey)
Log.d(TAG, "ADB RSA private key migrated")
}
Log.d(TAG, "AdbClientData migration completed")
}
@@ -464,6 +457,15 @@ class PreferenceMigration(private val appContext: Context) {
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(
key: String,
defaultValue: Boolean,
@@ -481,4 +483,82 @@ class PreferenceMigration(private val appContext: Context) {
val value = appSharedPrefs.getBoolean(key, defaultValue)
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"
}
}