fix: video orientation handling
- 用 StateFlow 实现监听复用
This commit is contained in:
@@ -23,15 +23,16 @@
|
|||||||
- 利用 mDNS 服务实现自动连接启用无线调试的设备、自动发现等待配对设备的IP与端口
|
- 利用 mDNS 服务实现自动连接启用无线调试的设备、自动发现等待配对设备的IP与端口
|
||||||
- 自动横竖屏切换(算吗
|
- 自动横竖屏切换(算吗
|
||||||
|
|
||||||
## 已知问题 / TODO
|
## 已知问题
|
||||||
|
|
||||||
- 退出全屏时的横竖屏状态可能不对,断开 scrcpy 重连就好
|
- 退出全屏时的横竖屏状态可能不对,断开 scrcpy 重连就好
|
||||||
- 如果受控机在锁屏时处理网络连接较慢,会导致应用界面无响应过长时间被系统杀掉(点名你米)
|
|
||||||
- 虚拟按键的截图实现方式为发送
|
- 虚拟按键的截图实现方式为发送
|
||||||
`keycode 120`,安卓官方([keycodes.h#349](https://android.googlesource.com/platform/frameworks/native/+/master/include/android/keycodes.h#349))的定义为
|
`keycode 120`,安卓官方([keycodes.h#349](https://android.googlesource.com/platform/frameworks/native/+/master/include/android/keycodes.h#349))的定义为
|
||||||
`System Request / Print Screen key.`,不同的厂商有不同的实现,在某些类原生(`AxionOS`) 上的行为是软重启
|
`System Request / Print Screen key.`,不同的厂商有不同的实现,在某些类原生(`AxionOS`) 上的行为是软重启
|
||||||
- I18N
|
|
||||||
- More: [TODO.md](TODO.md)
|
## [TODO](TODO.md)
|
||||||
|
|
||||||
|
\> [TODO.md](TODO.md)
|
||||||
|
|
||||||
## 建议搭配模块
|
## 建议搭配模块
|
||||||
|
|
||||||
|
|||||||
4
TODO.md
4
TODO.md
@@ -1,5 +1,8 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
- miuix v0.9
|
||||||
|
- 在线更新检查
|
||||||
|
|
||||||
## PARAMS
|
## PARAMS
|
||||||
|
|
||||||
- orientation locking
|
- orientation locking
|
||||||
@@ -18,3 +21,4 @@
|
|||||||
- [音频延迟](https://developer.android.com/ndk/guides/audio/audio-latency)
|
- [音频延迟](https://developer.android.com/ndk/guides/audio/audio-latency)
|
||||||
- 横屏布局
|
- 横屏布局
|
||||||
- 原生悬浮窗 [Petterpx/FloatingX](https://github.com/Petterpx/FloatingX)
|
- 原生悬浮窗 [Petterpx/FloatingX](https://github.com/Petterpx/FloatingX)
|
||||||
|
- I18N
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import androidx.compose.runtime.DisposableEffect
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
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
|
||||||
@@ -36,7 +35,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.scrcpy.Shared.Codec
|
|
||||||
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.SnackbarController
|
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
||||||
@@ -97,7 +95,7 @@ fun DeviceTabScreen(
|
|||||||
onOpenVirtualButtonOrder: () -> Unit,
|
onOpenVirtualButtonOrder: () -> Unit,
|
||||||
onOpenReorderDevices: () -> Unit,
|
onOpenReorderDevices: () -> Unit,
|
||||||
onOpenAdvancedPage: () -> Unit,
|
onOpenAdvancedPage: () -> Unit,
|
||||||
onOpenFullscreenPage: (Scrcpy.Session.SessionInfo) -> Unit,
|
onOpenFullscreenPage: () -> Unit,
|
||||||
) {
|
) {
|
||||||
var showThreePointMenu by rememberSaveable { mutableStateOf(false) }
|
var showThreePointMenu by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -159,7 +157,7 @@ fun DeviceTabPage(
|
|||||||
snackbar: SnackbarController,
|
snackbar: SnackbarController,
|
||||||
scrollBehavior: ScrollBehavior,
|
scrollBehavior: ScrollBehavior,
|
||||||
onOpenAdvancedPage: () -> Unit,
|
onOpenAdvancedPage: () -> Unit,
|
||||||
onOpenFullscreenPage: (Scrcpy.Session.SessionInfo) -> Unit,
|
onOpenFullscreenPage: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@@ -241,39 +239,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 { mutableIntStateOf(Defaults.ADB_PORT) }
|
var currentTargetPort by rememberSaveable { mutableStateOf(Defaults.ADB_PORT) }
|
||||||
var connectedDeviceLabel by rememberSaveable { mutableStateOf("未连接") }
|
var connectedDeviceLabel by rememberSaveable { mutableStateOf("未连接") }
|
||||||
var sessionInfoWidth by rememberSaveable { mutableIntStateOf(0) }
|
val sessionInfo by scrcpy.currentSessionState.collectAsState()
|
||||||
var sessionInfoHeight by rememberSaveable { mutableIntStateOf(0) }
|
|
||||||
var sessionInfoDeviceName by rememberSaveable { mutableStateOf("") }
|
|
||||||
var sessionInfoCodec by rememberSaveable { mutableStateOf<Codec?>(null) }
|
|
||||||
var sessionInfoControlEnabled by rememberSaveable { mutableStateOf(false) }
|
|
||||||
var sessionInfo by remember {
|
|
||||||
mutableStateOf<Scrcpy.Session.SessionInfo?>(null)
|
|
||||||
}
|
|
||||||
LaunchedEffect(
|
|
||||||
sessionInfoWidth,
|
|
||||||
sessionInfoHeight,
|
|
||||||
sessionInfoDeviceName,
|
|
||||||
sessionInfoCodec,
|
|
||||||
sessionInfoControlEnabled
|
|
||||||
) {
|
|
||||||
sessionInfo = if (sessionInfoDeviceName.isNotBlank()) {
|
|
||||||
Scrcpy.Session.SessionInfo(
|
|
||||||
width = sessionInfoWidth,
|
|
||||||
height = sessionInfoHeight,
|
|
||||||
deviceName = sessionInfoDeviceName,
|
|
||||||
codecId = 0,
|
|
||||||
codec = sessionInfoCodec,
|
|
||||||
audioCodecId = 0,
|
|
||||||
controlEnabled = sessionInfoControlEnabled,
|
|
||||||
host = currentTargetHost,
|
|
||||||
port = currentTargetPort,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var previewControlsVisible by rememberSaveable { mutableStateOf(false) }
|
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) }
|
||||||
@@ -349,7 +317,6 @@ fun DeviceTabPage(
|
|||||||
currentTargetPort = Defaults.ADB_PORT
|
currentTargetPort = Defaults.ADB_PORT
|
||||||
audioForwardingSupported = true
|
audioForwardingSupported = true
|
||||||
cameraMirroringSupported = true
|
cameraMirroringSupported = true
|
||||||
sessionInfo = null
|
|
||||||
statusLine = "未连接"
|
statusLine = "未连接"
|
||||||
connectedDeviceLabel = "未连接"
|
connectedDeviceLabel = "未连接"
|
||||||
clearQuickOnlineForTarget?.let { target ->
|
clearQuickOnlineForTarget?.let { target ->
|
||||||
@@ -702,32 +669,6 @@ fun DeviceTabPage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DisposableEffect(nativeCore) {
|
|
||||||
val listener: (Int, Int) -> Unit = { width, height ->
|
|
||||||
sessionInfo = sessionInfo?.copy(width = width, height = height)
|
|
||||||
}
|
|
||||||
nativeCore.addVideoSizeListener(listener)
|
|
||||||
onDispose {
|
|
||||||
nativeCore.removeVideoSizeListener(listener)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(sessionInfo) {
|
|
||||||
if (sessionInfo != null) {
|
|
||||||
sessionInfoWidth = sessionInfo?.width ?: 0
|
|
||||||
sessionInfoHeight = sessionInfo?.height ?: 0
|
|
||||||
sessionInfoDeviceName = sessionInfo?.deviceName.orEmpty()
|
|
||||||
sessionInfoCodec = sessionInfo?.codec
|
|
||||||
sessionInfoControlEnabled = sessionInfo?.controlEnabled == true
|
|
||||||
} else {
|
|
||||||
sessionInfoWidth = 0
|
|
||||||
sessionInfoHeight = 0
|
|
||||||
sessionInfoDeviceName = ""
|
|
||||||
sessionInfoCodec = null
|
|
||||||
sessionInfoControlEnabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sendVirtualButtonAction(action: VirtualButtonAction) {
|
fun sendVirtualButtonAction(action: VirtualButtonAction) {
|
||||||
val keycode = action.keycode ?: return
|
val keycode = action.keycode ?: return
|
||||||
runBusy("发送 ${action.title}") {
|
runBusy("发送 ${action.title}") {
|
||||||
@@ -942,10 +883,6 @@ fun DeviceTabPage(
|
|||||||
if (options.disableScreensaver) {
|
if (options.disableScreensaver) {
|
||||||
setKeepScreenOn(true)
|
setKeepScreenOn(true)
|
||||||
}
|
}
|
||||||
sessionInfo = session.copy(
|
|
||||||
host = currentTargetHost,
|
|
||||||
port = currentTargetPort
|
|
||||||
)
|
|
||||||
statusLine = "scrcpy 运行中"
|
statusLine = "scrcpy 运行中"
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
val videoDetail =
|
val videoDetail =
|
||||||
@@ -972,7 +909,6 @@ fun DeviceTabPage(
|
|||||||
runBusy("停止 scrcpy") {
|
runBusy("停止 scrcpy") {
|
||||||
scrcpy.stop()
|
scrcpy.stop()
|
||||||
setKeepScreenOn(false)
|
setKeepScreenOn(false)
|
||||||
sessionInfo = null
|
|
||||||
statusLine = "${currentTarget!!.host}:${currentTarget.port}"
|
statusLine = "${currentTarget!!.host}:${currentTarget.port}"
|
||||||
logEvent("scrcpy 已停止")
|
logEvent("scrcpy 已停止")
|
||||||
snackbar.show("scrcpy 已停止")
|
snackbar.show("scrcpy 已停止")
|
||||||
@@ -1013,8 +949,9 @@ fun DeviceTabPage(
|
|||||||
previewControlsVisible = !previewControlsVisible
|
previewControlsVisible = !previewControlsVisible
|
||||||
},
|
},
|
||||||
onOpenFullscreen = {
|
onOpenFullscreen = {
|
||||||
val info = sessionInfo ?: return@PreviewCard
|
if (sessionInfo != null) {
|
||||||
onOpenFullscreenPage(info)
|
onOpenFullscreenPage()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,14 +43,12 @@ import top.yukonga.miuix.kmp.basic.Scaffold
|
|||||||
@Composable
|
@Composable
|
||||||
fun FullscreenControlScreen(
|
fun FullscreenControlScreen(
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
session: Scrcpy.Session.SessionInfo,
|
scrcpy: Scrcpy,
|
||||||
nativeCore: NativeCoreFacade,
|
nativeCore: NativeCoreFacade,
|
||||||
onVideoSizeChanged: (width: Int, height: Int) -> Unit,
|
onVideoSizeChanged: (width: Int, height: Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
// Disable predictive back handler temporarily to avoid decoding issues.
|
|
||||||
BackHandler(enabled = true, onBack = onBack)
|
BackHandler(enabled = true, onBack = onBack)
|
||||||
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
val haptics = rememberAppHaptics()
|
val haptics = rememberAppHaptics()
|
||||||
@@ -58,6 +56,7 @@ fun FullscreenControlScreen(
|
|||||||
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
||||||
|
|
||||||
val activity = remember(context) { context as? Activity }
|
val activity = remember(context) { context as? Activity }
|
||||||
|
val currentSession by scrcpy.currentSessionState.collectAsState()
|
||||||
|
|
||||||
val asBundleShared by appSettings.bundleState.collectAsState()
|
val asBundleShared by appSettings.bundleState.collectAsState()
|
||||||
val asBundleSharedLatest by rememberUpdatedState(asBundleShared)
|
val asBundleSharedLatest by rememberUpdatedState(asBundleShared)
|
||||||
@@ -119,14 +118,9 @@ fun FullscreenControlScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DisposableEffect(nativeCore) {
|
LaunchedEffect(currentSession?.width, currentSession?.height) {
|
||||||
val listener: (Int, Int) -> Unit = { w, h ->
|
val session = currentSession ?: return@LaunchedEffect
|
||||||
onVideoSizeChanged(w, h)
|
onVideoSizeChanged(session.width, session.height)
|
||||||
}
|
|
||||||
nativeCore.addVideoSizeListener(listener)
|
|
||||||
onDispose {
|
|
||||||
nativeCore.removeVideoSizeListener(listener)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DisposableEffect(nativeCore) {
|
DisposableEffect(nativeCore) {
|
||||||
@@ -156,6 +150,7 @@ fun FullscreenControlScreen(
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(contentPadding),
|
.padding(contentPadding),
|
||||||
) {
|
) {
|
||||||
|
val session = currentSession ?: return@Box
|
||||||
FullscreenControlScreen(
|
FullscreenControlScreen(
|
||||||
session = session,
|
session = session,
|
||||||
nativeCore = nativeCore,
|
nativeCore = nativeCore,
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ sealed interface RootScreen : NavKey {
|
|||||||
data object Home : RootScreen
|
data object Home : RootScreen
|
||||||
data object Advanced : RootScreen
|
data object Advanced : RootScreen
|
||||||
data object VirtualButtonOrder : RootScreen
|
data object VirtualButtonOrder : RootScreen
|
||||||
data class Fullscreen(val session: Scrcpy.Session.SessionInfo) : RootScreen
|
data object Fullscreen : RootScreen
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -215,6 +215,7 @@ fun MainScreen() {
|
|||||||
serverRemotePath = serverRemotePath,
|
serverRemotePath = serverRemotePath,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val currentSession by scrcpy.currentSessionState.collectAsState()
|
||||||
|
|
||||||
fun handleBackNavigation() {
|
fun handleBackNavigation() {
|
||||||
if (rootBackStack.size > 1) {
|
if (rootBackStack.size > 1) {
|
||||||
@@ -247,6 +248,11 @@ fun MainScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var showReorderDevices by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var fullscreenOrientation by rememberSaveable {
|
||||||
|
mutableIntStateOf(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
|
||||||
|
}
|
||||||
|
|
||||||
val canNavigateBack = rootBackStack.size > 1
|
val canNavigateBack = rootBackStack.size > 1
|
||||||
|| pagerState.currentPage != MainBottomTabDestination.Device.ordinal
|
|| pagerState.currentPage != MainBottomTabDestination.Device.ordinal
|
||||||
|
|
||||||
@@ -265,11 +271,6 @@ fun MainScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var showReorderDevices by rememberSaveable { mutableStateOf(false) }
|
|
||||||
var fullscreenOrientation by rememberSaveable {
|
|
||||||
mutableIntStateOf(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
||||||
@@ -279,6 +280,28 @@ fun MainScreen() {
|
|||||||
activity?.requestedOrientation = targetOrientation
|
activity?.requestedOrientation = targetOrientation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisposableEffect(nativeCore, scrcpy) {
|
||||||
|
val listener: (Int, Int) -> Unit = { width, height ->
|
||||||
|
scrcpy.updateCurrentSessionSize(width, height)
|
||||||
|
}
|
||||||
|
nativeCore.addVideoSizeListener(listener)
|
||||||
|
onDispose {
|
||||||
|
nativeCore.removeVideoSizeListener(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(currentRootScreen, currentSession?.width, currentSession?.height) {
|
||||||
|
if (currentRootScreen is RootScreen.Fullscreen) {
|
||||||
|
val session = currentSession ?: return@LaunchedEffect
|
||||||
|
fullscreenOrientation =
|
||||||
|
if (session.width >= session.height) {
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||||
|
} else {
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(asBundle.adbKeyName) {
|
LaunchedEffect(asBundle.adbKeyName) {
|
||||||
adbService.keyName = asBundle.adbKeyName.ifBlank { AppSettings.ADB_KEY_NAME.defaultValue }
|
adbService.keyName = asBundle.adbKeyName.ifBlank { AppSettings.ADB_KEY_NAME.defaultValue }
|
||||||
}
|
}
|
||||||
@@ -341,16 +364,16 @@ fun MainScreen() {
|
|||||||
onOpenVirtualButtonOrder = { rootBackStack.add(RootScreen.VirtualButtonOrder) },
|
onOpenVirtualButtonOrder = { rootBackStack.add(RootScreen.VirtualButtonOrder) },
|
||||||
onOpenReorderDevices = { showReorderDevices = true },
|
onOpenReorderDevices = { showReorderDevices = true },
|
||||||
onOpenAdvancedPage = { rootBackStack.add(RootScreen.Advanced) },
|
onOpenAdvancedPage = { rootBackStack.add(RootScreen.Advanced) },
|
||||||
onOpenFullscreenPage = { session ->
|
onOpenFullscreenPage = {
|
||||||
|
currentSession?.let { session ->
|
||||||
fullscreenOrientation =
|
fullscreenOrientation =
|
||||||
if (session.width >= session.height) {
|
if (session.width >= session.height) {
|
||||||
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||||
} else {
|
} else {
|
||||||
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||||
}
|
}
|
||||||
rootBackStack.add(
|
rootBackStack.add(RootScreen.Fullscreen)
|
||||||
RootScreen.Fullscreen(session),
|
}
|
||||||
)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -396,10 +419,10 @@ fun MainScreen() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry<RootScreen.Fullscreen> { screen ->
|
entry(RootScreen.Fullscreen) {
|
||||||
FullscreenControlScreen(
|
FullscreenControlScreen(
|
||||||
onBack = ::popRoot,
|
onBack = ::popRoot,
|
||||||
session = screen.session,
|
scrcpy = scrcpy,
|
||||||
nativeCore = nativeCore,
|
nativeCore = nativeCore,
|
||||||
onVideoSizeChanged = { width, height ->
|
onVideoSizeChanged = { width, height ->
|
||||||
fullscreenOrientation =
|
fullscreenOrientation =
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import io.github.miuzarte.scrcpyforandroid.scrcpy.Shared.EncoderType
|
|||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Shared.ListOptions
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Shared.ListOptions
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.EventLogger.logEvent
|
import io.github.miuzarte.scrcpyforandroid.services.EventLogger.logEvent
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -58,8 +61,8 @@ class Scrcpy(
|
|||||||
private val session = Session(adbService)
|
private val session = Session(adbService)
|
||||||
private val nativeCore: NativeCoreFacade = NativeCoreFacade.get(appContext)
|
private val nativeCore: NativeCoreFacade = NativeCoreFacade.get(appContext)
|
||||||
|
|
||||||
@Volatile
|
private val _currentSessionState = MutableStateFlow<Session.SessionInfo?>(null)
|
||||||
private var currentSession: Session.SessionInfo? = null
|
val currentSessionState: StateFlow<Session.SessionInfo?> = _currentSessionState.asStateFlow()
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var isRunning: Boolean = false
|
private var isRunning: Boolean = false
|
||||||
@@ -136,7 +139,7 @@ class Scrcpy(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create session info
|
// Create session info
|
||||||
currentSession = info
|
_currentSessionState.value = info
|
||||||
isRunning = true
|
isRunning = true
|
||||||
|
|
||||||
// Setup video consumer (notify NativeCoreFacade to setup decoders)
|
// Setup video consumer (notify NativeCoreFacade to setup decoders)
|
||||||
@@ -175,7 +178,7 @@ class Scrcpy(
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "start(): Failed to start scrcpy session", e)
|
Log.e(TAG, "start(): Failed to start scrcpy session", e)
|
||||||
isRunning = false
|
isRunning = false
|
||||||
currentSession = null
|
_currentSessionState.value = null
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +199,7 @@ class Scrcpy(
|
|||||||
audioPlayer?.release()
|
audioPlayer?.release()
|
||||||
audioPlayer = null
|
audioPlayer = null
|
||||||
isRunning = false
|
isRunning = false
|
||||||
currentSession = null
|
_currentSessionState.value = null
|
||||||
Log.i(TAG, "stop(): Session stopped successfully")
|
Log.i(TAG, "stop(): Session stopped successfully")
|
||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -212,7 +215,13 @@ class Scrcpy(
|
|||||||
|
|
||||||
fun isStarted(): Boolean = isRunning && session.isStarted()
|
fun isStarted(): Boolean = isRunning && session.isStarted()
|
||||||
|
|
||||||
fun getCurrentSession(): Session.SessionInfo? = currentSession
|
fun getCurrentSession(): Session.SessionInfo? = currentSessionState.value
|
||||||
|
|
||||||
|
fun updateCurrentSessionSize(width: Int, height: Int) {
|
||||||
|
val current = _currentSessionState.value ?: return
|
||||||
|
if (current.width == width && current.height == height) return
|
||||||
|
_currentSessionState.value = current.copy(width = width, height = height)
|
||||||
|
}
|
||||||
|
|
||||||
sealed class ListResult {
|
sealed class ListResult {
|
||||||
data class Encoders(
|
data class Encoders(
|
||||||
|
|||||||
Reference in New Issue
Block a user