feat: scrcpy following with auto fullscreen

This commit is contained in:
Miuzarte
2026-04-11 19:54:08 +08:00
parent a4e6027097
commit 69e1976e5b
7 changed files with 122 additions and 51 deletions

View File

@@ -33,4 +33,9 @@ class MainActivity : ComponentActivity() {
MainScreen() MainScreen()
} }
} }
override fun onResume() {
super.onResume()
StreamActivity.dismissActivePictureInPicture()
}
} }

View File

@@ -1,5 +1,6 @@
package io.github.miuzarte.scrcpyforandroid package io.github.miuzarte.scrcpyforandroid
import android.R.drawable
import android.app.PictureInPictureUiState import android.app.PictureInPictureUiState
import android.app.RemoteAction import android.app.RemoteAction
import android.content.Context import android.content.Context
@@ -8,16 +9,18 @@ import android.graphics.drawable.Icon
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.core.app.PictureInPictureParamsCompat import androidx.core.app.PictureInPictureParamsCompat.Builder
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.pip.BasicPictureInPicture import androidx.core.pip.BasicPictureInPicture
import io.github.miuzarte.scrcpyforandroid.pages.StreamScreen import io.github.miuzarte.scrcpyforandroid.pages.StreamScreen
import io.github.miuzarte.scrcpyforandroid.services.PictureInPictureActionReceiver import io.github.miuzarte.scrcpyforandroid.services.PictureInPictureActionReceiver
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import java.lang.ref.WeakReference
class StreamActivity : ComponentActivity() { class StreamActivity : ComponentActivity() {
private val basicPip = BasicPictureInPicture(this) private val basicPip = BasicPictureInPicture(this)
private val pipActionReceiver = PictureInPictureActionReceiver() private val pipActionReceiver = PictureInPictureActionReceiver()
private var isPipActionReceiverRegistered = false private var isPipActionReceiverRegistered = false
@@ -28,7 +31,7 @@ class StreamActivity : ComponentActivity() {
val pipStopAction: RemoteAction by lazy { val pipStopAction: RemoteAction by lazy {
RemoteAction( RemoteAction(
Icon.createWithResource(this, android.R.drawable.ic_menu_close_clear_cancel), Icon.createWithResource(this, drawable.ic_menu_close_clear_cancel),
"停止投屏", "停止投屏",
"停止投屏", "停止投屏",
PictureInPictureActionReceiver.createPendingIntent(this), PictureInPictureActionReceiver.createPendingIntent(this),
@@ -39,6 +42,7 @@ class StreamActivity : ComponentActivity() {
// 都会重建 activity // 都会重建 activity
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
currentActivityRef = WeakReference(this)
registerPipActionReceiver() registerPipActionReceiver()
@@ -76,11 +80,13 @@ class StreamActivity : ComponentActivity() {
*/ */
} }
fun configurePip(params: PictureInPictureParamsCompat) { fun configurePip(block: Builder.() -> Builder) =
basicPip.setPictureInPictureParams(params) basicPip.setPictureInPictureParams(Builder().block().build())
}
override fun onDestroy() { override fun onDestroy() {
currentActivityRef?.get()
?.takeIf { it === this }
?.let { currentActivityRef = null }
unregisterPipActionReceiver() unregisterPipActionReceiver()
super.onDestroy() super.onDestroy()
} }
@@ -138,8 +144,16 @@ class StreamActivity : ComponentActivity() {
} }
companion object { companion object {
private var currentActivityRef: WeakReference<StreamActivity>? = null
fun createIntent(context: Context): Intent { fun createIntent(context: Context): Intent {
return Intent(context, StreamActivity::class.java) return Intent(context, StreamActivity::class.java)
} }
fun dismissActivePictureInPicture() {
currentActivityRef?.get()
?.takeIf { it.isInPictureInPictureMode }
?.finish()
}
} }
} }

View File

@@ -39,6 +39,7 @@ class DeviceShortcuts(val devices: List<DeviceShortcut>) : List<DeviceShortcut>
port: Int? = null, port: Int? = null,
name: String? = null, name: String? = null,
startScrcpyOnConnect: Boolean? = null, startScrcpyOnConnect: Boolean? = null,
openFullscreenOnStart: Boolean? = null,
newPort: Int? = null, newPort: Int? = null,
updateNameOnlyWhenEmpty: Boolean = false, updateNameOnlyWhenEmpty: Boolean = false,
): DeviceShortcuts { ): DeviceShortcuts {
@@ -59,6 +60,7 @@ class DeviceShortcuts(val devices: List<DeviceShortcut>) : List<DeviceShortcut>
val finalHost = if (updateById) host ?: old.host else old.host val finalHost = if (updateById) host ?: old.host else old.host
val finalPort = if (updateById) port ?: old.port else newPort ?: old.port val finalPort = if (updateById) port ?: old.port else newPort ?: old.port
val finalStartScrcpyOnConnect = startScrcpyOnConnect ?: old.startScrcpyOnConnect val finalStartScrcpyOnConnect = startScrcpyOnConnect ?: old.startScrcpyOnConnect
val finalOpenFullscreenOnStart = openFullscreenOnStart ?: old.openFullscreenOnStart
// 若无任何变化,返回原实例 // 若无任何变化,返回原实例
if ( if (
@@ -66,6 +68,7 @@ class DeviceShortcuts(val devices: List<DeviceShortcut>) : List<DeviceShortcut>
&& finalHost == old.host && finalHost == old.host
&& finalPort == old.port && finalPort == old.port
&& finalStartScrcpyOnConnect == old.startScrcpyOnConnect && finalStartScrcpyOnConnect == old.startScrcpyOnConnect
&& finalOpenFullscreenOnStart == old.openFullscreenOnStart
) )
return this return this
@@ -75,6 +78,7 @@ class DeviceShortcuts(val devices: List<DeviceShortcut>) : List<DeviceShortcut>
host = finalHost, host = finalHost,
port = finalPort, port = finalPort,
startScrcpyOnConnect = finalStartScrcpyOnConnect, startScrcpyOnConnect = finalStartScrcpyOnConnect,
openFullscreenOnStart = finalOpenFullscreenOnStart,
) )
} }
return DeviceShortcuts( return DeviceShortcuts(
@@ -128,13 +132,18 @@ data class DeviceShortcut(
val host: String, val host: String,
val port: Int = Defaults.ADB_PORT, val port: Int = Defaults.ADB_PORT,
val startScrcpyOnConnect: Boolean = false, val startScrcpyOnConnect: Boolean = false,
val openFullscreenOnStart: Boolean = false,
) { ) {
val id: String get() = "$host:$port" val id: String get() = "$host:$port"
fun marshalToString( fun marshalToString(
separator: String = DEFAULT_SEPARATOR, separator: String = DEFAULT_SEPARATOR,
): String = listOf( ): String = listOf(
name.trim(), host.trim(), port.toString(), if (startScrcpyOnConnect) "1" else "0" name.trim(),
host.trim(),
port.toString(),
if (startScrcpyOnConnect) "1" else "0",
if (openFullscreenOnStart) "1" else "0",
).joinToString( ).joinToString(
separator = separator separator = separator
) )
@@ -147,16 +156,19 @@ data class DeviceShortcut(
): DeviceShortcut? { ): DeviceShortcut? {
val parts = s.split(separator) val parts = s.split(separator)
return when (parts.size) { return when (parts.size) {
3, 4 -> { 3, 4, 5 -> {
val name = parts[0].trim() val name = parts[0].trim()
val host = parts[1].trim() val host = parts[1].trim()
val port = parts[2].trim().toIntOrNull() ?: Defaults.ADB_PORT val port = parts[2].trim().toIntOrNull() ?: Defaults.ADB_PORT
val startScrcpyOnConnect = parts.getOrNull(3)?.trim() == "1" val startScrcpyOnConnect = parts.getOrNull(3)?.trim() == "1"
val openFullscreenOnStart =
startScrcpyOnConnect && parts.getOrNull(4)?.trim() == "1"
if (host.isNotBlank()) DeviceShortcut( if (host.isNotBlank()) DeviceShortcut(
name = name, name = name,
host = host, host = host,
port = port, port = port,
startScrcpyOnConnect = startScrcpyOnConnect, startScrcpyOnConnect = startScrcpyOnConnect,
openFullscreenOnStart = openFullscreenOnStart,
) )
else null else null
} }

View File

@@ -18,6 +18,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@@ -37,7 +38,6 @@ import io.github.miuzarte.scrcpyforandroid.models.DeviceShortcuts
import io.github.miuzarte.scrcpyforandroid.nativecore.NativeAdbService import io.github.miuzarte.scrcpyforandroid.nativecore.NativeAdbService
import io.github.miuzarte.scrcpyforandroid.scaffolds.LazyColumn import io.github.miuzarte.scrcpyforandroid.scaffolds.LazyColumn
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
import io.github.miuzarte.scrcpyforandroid.services.AppRuntime
import io.github.miuzarte.scrcpyforandroid.services.EventLogger import io.github.miuzarte.scrcpyforandroid.services.EventLogger
import io.github.miuzarte.scrcpyforandroid.services.EventLogger.logEvent import io.github.miuzarte.scrcpyforandroid.services.EventLogger.logEvent
import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController
@@ -232,10 +232,9 @@ fun DeviceTabPage(
var adbConnected by rememberSaveable { mutableStateOf(false) } var adbConnected by rememberSaveable { mutableStateOf(false) }
var isQuickConnected by rememberSaveable { mutableStateOf(false) } var isQuickConnected by rememberSaveable { mutableStateOf(false) }
var currentTargetHost by rememberSaveable { mutableStateOf("") } var currentTargetHost by rememberSaveable { mutableStateOf("") }
var currentTargetPort by rememberSaveable { mutableStateOf(Defaults.ADB_PORT) } var currentTargetPort by rememberSaveable { mutableIntStateOf(Defaults.ADB_PORT) }
var connectedDeviceLabel by rememberSaveable { mutableStateOf("未连接") } var connectedDeviceLabel by rememberSaveable { mutableStateOf("未连接") }
val sessionInfo by scrcpy.currentSessionState.collectAsState() val sessionInfo by scrcpy.currentSessionState.collectAsState()
var previewControlsVisible by rememberSaveable { mutableStateOf(false) }
var editingDeviceId by rememberSaveable { mutableStateOf<String?>(null) } var editingDeviceId by rememberSaveable { mutableStateOf<String?>(null) }
var activeDeviceActionId by rememberSaveable { mutableStateOf<String?>(null) } var activeDeviceActionId by rememberSaveable { mutableStateOf<String?>(null) }
var adbConnecting by rememberSaveable { mutableStateOf(false) } var adbConnecting by rememberSaveable { mutableStateOf(false) }
@@ -277,9 +276,6 @@ fun DeviceTabPage(
qdBundle = qdBundle.copy(quickDevicesList = serialized) qdBundle = qdBundle.copy(quickDevicesList = serialized)
} }
} }
val editingDevice = remember(savedShortcuts, editingDeviceId) {
editingDeviceId?.let(savedShortcuts::get)
}
/** /**
* Disconnect the current ADB connection and stop any running scrcpy session. * Disconnect the current ADB connection and stop any running scrcpy session.
@@ -400,10 +396,7 @@ fun DeviceTabPage(
suspend fun keepAliveCheck(host: String, port: Int): Boolean { suspend fun keepAliveCheck(host: String, port: Int): Boolean {
return withTimeout(ADB_KEEPALIVE_TIMEOUT_MS) { return withTimeout(ADB_KEEPALIVE_TIMEOUT_MS) {
val connected = NativeAdbService.isConnected() val connected = NativeAdbService.isConnected()
if (!connected) { return@withTimeout connected
return@withTimeout false
}
return@withTimeout true
} }
} }
@@ -536,10 +529,15 @@ fun DeviceTabPage(
} }
} }
suspend fun startScrcpySession() { suspend fun startScrcpySession(openFullscreen: Boolean = false) {
val options = scrcpyOptions.toClientOptions(soBundleShared).fix() val options = scrcpyOptions.toClientOptions(soBundleShared).fix()
val session = scrcpy.start(options) val session = scrcpy.start(options)
pendingScrollToPreview = true pendingScrollToPreview = true
if (openFullscreen) {
withContext(Dispatchers.Main) {
context.startActivity(StreamActivity.createIntent(context))
}
}
if (options.disableScreensaver) { if (options.disableScreensaver) {
setKeepScreenOn(true) setKeepScreenOn(true)
} }
@@ -571,7 +569,11 @@ fun DeviceTabPage(
listState.animateScrollToItem(PREVIEW_CARD_ITEM_INDEX) listState.animateScrollToItem(PREVIEW_CARD_ITEM_INDEX)
} }
suspend fun handleAdbConnected(host: String, port: Int) { suspend fun handleAdbConnected(
host: String, port: Int,
autoStartScrcpy: Boolean = false,
autoEnterFullScreen: Boolean = false,
) {
currentTargetHost = host currentTargetHost = host
currentTargetPort = port currentTargetPort = port
@@ -603,15 +605,11 @@ fun DeviceTabPage(
) )
snackbar.show("ADB 已连接") snackbar.show("ADB 已连接")
if ( if (autoStartScrcpy && sessionInfo == null) runBusy("启动 scrcpy") {
savedShortcuts.get(host, port)?.startScrcpyOnConnect == true && startScrcpySession(openFullscreen = autoStartScrcpy && autoEnterFullScreen)
sessionInfo == null
) {
runBusy("启动 scrcpy") {
startScrcpySession()
}
} }
} }
LaunchedEffect( LaunchedEffect(
adbConnected, adbConnected,
asBundle.adbAutoReconnectPairedDevice, asBundle.adbAutoReconnectPairedDevice,
@@ -751,8 +749,9 @@ fun DeviceTabPage(
adbConnecting && activeDeviceActionId == device.id adbConnecting && activeDeviceActionId == device.id
}, },
editingDeviceId = editingDeviceId, editingDeviceId = editingDeviceId,
onClick = { onClick = { device ->
snackbar.show("长按可编辑") if (editingDeviceId != device.id)
snackbar.show("长按可编辑")
}, },
onLongClick = { device -> onLongClick = { device ->
val connected = adbConnected val connected = adbConnected
@@ -768,11 +767,14 @@ fun DeviceTabPage(
}, },
onAction = { device -> onAction = { device ->
haptics.contextClick() haptics.contextClick()
if (editingDeviceId == device.id) editingDeviceId = null
val host = device.host val host = device.host
val port = device.port val port = device.port
val connected = adbConnected val connected = adbConnected
&& currentTarget?.host == host && currentTarget?.host == host
&& currentTarget.port == port && currentTarget.port == port
if (!connected) { if (!connected) {
runAdbConnect( runAdbConnect(
"连接 ADB", "连接 ADB",
@@ -787,7 +789,12 @@ fun DeviceTabPage(
savedShortcuts = savedShortcuts.update( savedShortcuts = savedShortcuts.update(
host = host, port = port, host = host, port = port,
) )
handleAdbConnected(host, port) handleAdbConnected(
host = host, port = port,
autoStartScrcpy = device.startScrcpyOnConnect,
autoEnterFullScreen = device.startScrcpyOnConnect
&& device.openFullscreenOnStart,
)
} catch (e: Exception) { } catch (e: Exception) {
statusLine = "ADB 连接失败" statusLine = "ADB 连接失败"
logEvent("ADB 连接失败: $e", Log.ERROR) logEvent("ADB 连接失败: $e", Log.ERROR)
@@ -817,6 +824,7 @@ fun DeviceTabPage(
host = updated.host, host = updated.host,
port = updated.port, port = updated.port,
startScrcpyOnConnect = updated.startScrcpyOnConnect, startScrcpyOnConnect = updated.startScrcpyOnConnect,
openFullscreenOnStart = updated.openFullscreenOnStart,
) )
}, },
onEditorDelete = { device -> onEditorDelete = { device ->
@@ -962,10 +970,6 @@ fun DeviceTabPage(
modifier = Modifier, modifier = Modifier,
sessionInfo = sessionInfo, sessionInfo = sessionInfo,
previewHeightDp = asBundle.devicePreviewCardHeightDp.coerceAtLeast(120), previewHeightDp = asBundle.devicePreviewCardHeightDp.coerceAtLeast(120),
controlsVisible = previewControlsVisible,
onTapped = {
previewControlsVisible = !previewControlsVisible
},
onOpenFullscreen = { onOpenFullscreen = {
context.startActivity(StreamActivity.createIntent(context)) context.startActivity(StreamActivity.createIntent(context))
}, },

View File

@@ -955,7 +955,7 @@ internal fun ScrcpyAllOptionsPage(
} }
}, },
) )
// TODO: 在 OverlaySpinnerPreference / OverlayDropdownPreference 支持展开状态回调后, 在展开时触发获取 // TODO: 等 MIUIX 发版, 在 OverlaySpinnerPreference / OverlayDropdownPreference 支持展开状态回调后, 在展开时触发获取
OverlaySpinnerPreference( OverlaySpinnerPreference(
title = "视频编码器", title = "视频编码器",
summary = "--video-encoder", summary = "--video-encoder",

View File

@@ -12,7 +12,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.core.app.PictureInPictureParamsCompat
import io.github.miuzarte.scrcpyforandroid.StreamActivity import io.github.miuzarte.scrcpyforandroid.StreamActivity
import io.github.miuzarte.scrcpyforandroid.constants.ThemeModes import io.github.miuzarte.scrcpyforandroid.constants.ThemeModes
import io.github.miuzarte.scrcpyforandroid.haptics.LocalAppHaptics import io.github.miuzarte.scrcpyforandroid.haptics.LocalAppHaptics
@@ -72,15 +71,13 @@ fun StreamScreen(activity: StreamActivity) {
lastPipAspectRatio = ratio lastPipAspectRatio = ratio
} }
activity.configurePip( activity.configurePip {
PictureInPictureParamsCompat.Builder() setEnabled(true)
.setEnabled(true) setAspectRatio(pipAspectRatio)
.setAspectRatio(pipAspectRatio) setSourceRectHint(pipSourceRectHint)
.setSourceRectHint(pipSourceRectHint) setSeamlessResizeEnabled(true)
.setSeamlessResizeEnabled(true) setCloseAction(activity.pipStopAction)
.setCloseAction(activity.pipStopAction) }
.build(),
)
} }
val themeMode = val themeMode =

View File

@@ -23,10 +23,10 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
@@ -250,14 +250,17 @@ internal fun PreviewCard(
modifier: Modifier, modifier: Modifier,
sessionInfo: Scrcpy.Session.SessionInfo?, sessionInfo: Scrcpy.Session.SessionInfo?,
previewHeightDp: Int, previewHeightDp: Int,
controlsVisible: Boolean,
onTapped: () -> Unit,
onOpenFullscreen: () -> Unit, onOpenFullscreen: () -> Unit,
autoBringIntoView: Boolean = false, autoBringIntoView: Boolean = false,
onAutoBringIntoViewConsumed: () -> Unit = {}, onAutoBringIntoViewConsumed: () -> Unit = {},
) { ) {
val haptics = rememberAppHaptics() val haptics = rememberAppHaptics()
val alpha by animateFloatAsState(if (controlsVisible) 1f else 0f, label = "preview-controls")
var previewControlsVisible by rememberSaveable { mutableStateOf(false) }
val alpha by animateFloatAsState(
if (previewControlsVisible) 1f else 0f,
label = "preview-controls"
)
val lifecycleOwner = LocalLifecycleOwner.current val lifecycleOwner = LocalLifecycleOwner.current
val bringIntoViewRequester = remember { BringIntoViewRequester() } val bringIntoViewRequester = remember { BringIntoViewRequester() }
@@ -268,6 +271,9 @@ internal fun PreviewCard(
} }
DisposableEffect(lifecycleOwner) { DisposableEffect(lifecycleOwner) {
if (lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
VideoOutputTargetState.set(VideoOutputTarget.PREVIEW)
}
val observer = LifecycleEventObserver { _, event -> val observer = LifecycleEventObserver { _, event ->
when (event) { when (event) {
Lifecycle.Event.ON_START -> VideoOutputTargetState.set(VideoOutputTarget.PREVIEW) Lifecycle.Event.ON_START -> VideoOutputTargetState.set(VideoOutputTarget.PREVIEW)
@@ -287,12 +293,20 @@ internal fun PreviewCard(
} }
} }
Card(modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester).then(modifier)) { Card(
modifier = Modifier
.bringIntoViewRequester(bringIntoViewRequester)
.then(modifier)
) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(previewHeightDp.coerceAtLeast(120).dp) .height(previewHeightDp.coerceAtLeast(120).dp)
.pointerInput(sessionInfo) { detectTapGestures(onTap = { onTapped() }) }, .pointerInput(sessionInfo) {
detectTapGestures(onTap = {
previewControlsVisible = !previewControlsVisible
})
},
) { ) {
BoxWithConstraints(modifier = Modifier.fillMaxSize()) { BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
val sessionAspect = val sessionAspect =
@@ -801,6 +815,17 @@ fun ScrcpyVideoSurface(
} }
DisposableEffect(lifecycleOwner, session, currentSurface) { DisposableEffect(lifecycleOwner, session, currentSurface) {
val surface = currentSurface
if (
lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) &&
session != null &&
surface != null &&
surface.isValid
) {
scope.launch {
NativeCoreFacade.attachVideoSurface(surface)
}
}
val observer = LifecycleEventObserver { _, event -> val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) { if (event == Lifecycle.Event.ON_START) {
val surface = currentSurface val surface = currentSurface
@@ -916,6 +941,8 @@ internal fun DeviceTile(
host = trimmedHost, host = trimmedHost,
port = currentDraft.port, port = currentDraft.port,
startScrcpyOnConnect = currentDraft.startScrcpyOnConnect, startScrcpyOnConnect = currentDraft.startScrcpyOnConnect,
openFullscreenOnStart = currentDraft.startScrcpyOnConnect
&& currentDraft.openFullscreenOnStart,
) )
if (updated != device) { if (updated != device) {
onEditorSave(updated) onEditorSave(updated)
@@ -1048,6 +1075,18 @@ internal fun DeviceTile(
draft = currentDraft.copy(startScrcpyOnConnect = it) draft = currentDraft.copy(startScrcpyOnConnect = it)
}, },
) )
AnimatedVisibility(currentDraft.startScrcpyOnConnect) {
CheckboxPreference(
title = "直接进入全屏",
checkboxLocation = CheckboxLocation.End,
checked = currentDraft.startScrcpyOnConnect
&& currentDraft.openFullscreenOnStart,
enabled = currentDraft.startScrcpyOnConnect,
onCheckedChange = {
draft = currentDraft.copy(openFullscreenOnStart = it)
},
)
}
} }
Row( Row(
modifier = Modifier modifier = Modifier