fix: double back quitting
连接时导致界面无响应的问题没修好
This commit is contained in:
@@ -26,6 +26,12 @@
|
|||||||
|
|
||||||
- 组件排序动画一坨
|
- 组件排序动画一坨
|
||||||
- 退出全屏时的横竖屏状态可能不对,断开 scrcpy 重连就好
|
- 退出全屏时的横竖屏状态可能不对,断开 scrcpy 重连就好
|
||||||
|
- 如果受控机在锁屏时处理网络连接较慢,会导致应用界面无响应过长时间被系统杀掉(点名你米)
|
||||||
|
|
||||||
|
## 建议搭配模块
|
||||||
|
|
||||||
|
- 密码锁屏无法捕获: [LSPosed/DisableFlagSecure](https://github.com/LSPosed/DisableFlagSecure)
|
||||||
|
- 开机自动启用 adb: [gist/906291](https://gist.github.com/Miuzarte/9062915f1615d5eebd363c759fda496c)
|
||||||
|
|
||||||
## 构建
|
## 构建
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
@@ -26,6 +25,9 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<!-- <profileable android:shell="true" /> -->
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
package io.github.miuzarte.scrcpyforandroid.pages
|
package io.github.miuzarte.scrcpyforandroid.pages
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.WindowManager
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
@@ -61,6 +59,7 @@ import io.github.miuzarte.scrcpyforandroid.widgets.VirtualButtonActions
|
|||||||
import io.github.miuzarte.scrcpyforandroid.widgets.VirtualButtonCard
|
import io.github.miuzarte.scrcpyforandroid.widgets.VirtualButtonCard
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.TimeoutCancellationException
|
import kotlinx.coroutines.TimeoutCancellationException
|
||||||
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -73,6 +72,7 @@ import java.net.Socket
|
|||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import java.util.concurrent.Executors
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
private const val ADB_CONNECT_TIMEOUT_MS = 3_000L
|
private const val ADB_CONNECT_TIMEOUT_MS = 3_000L
|
||||||
@@ -125,7 +125,6 @@ fun DeviceTabScreen(
|
|||||||
nativeCore: NativeCoreFacade,
|
nativeCore: NativeCoreFacade,
|
||||||
snack: SnackbarHostState,
|
snack: SnackbarHostState,
|
||||||
scrollBehavior: ScrollBehavior,
|
scrollBehavior: ScrollBehavior,
|
||||||
keepScreenOnWhenStreamingEnabled: Boolean,
|
|
||||||
virtualButtonsOutside: List<String>,
|
virtualButtonsOutside: List<String>,
|
||||||
virtualButtonsInMore: List<String>,
|
virtualButtonsInMore: List<String>,
|
||||||
previewCardHeightDp: Int,
|
previewCardHeightDp: Int,
|
||||||
@@ -222,9 +221,19 @@ fun DeviceTabScreen(
|
|||||||
val virtualButtonLayout = remember(virtualButtonsOutside, virtualButtonsInMore) {
|
val virtualButtonLayout = remember(virtualButtonsOutside, virtualButtonsInMore) {
|
||||||
VirtualButtonActions.resolveLayout(virtualButtonsOutside, virtualButtonsInMore)
|
VirtualButtonActions.resolveLayout(virtualButtonsOutside, virtualButtonsInMore)
|
||||||
}
|
}
|
||||||
val activity = remember(context) { context as? Activity }
|
|
||||||
val initialSettings = remember(context) { loadDevicePageSettings(context) }
|
val initialSettings = remember(context) { loadDevicePageSettings(context) }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
val adbWorkerDispatcher = remember {
|
||||||
|
Executors.newSingleThreadExecutor { runnable ->
|
||||||
|
Thread(runnable, "adb-connect-worker").apply { isDaemon = true }
|
||||||
|
}.asCoroutineDispatcher()
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposableEffect(adbWorkerDispatcher) {
|
||||||
|
onDispose {
|
||||||
|
adbWorkerDispatcher.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var busy by rememberSaveable { mutableStateOf(false) }
|
var busy by rememberSaveable { mutableStateOf(false) }
|
||||||
var statusLine by rememberSaveable { mutableStateOf("未连接") }
|
var statusLine by rememberSaveable { mutableStateOf("未连接") }
|
||||||
@@ -315,6 +324,46 @@ fun DeviceTabScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun disconnectAdbConnection(
|
||||||
|
clearQuickOnlineForTarget: ConnectionTarget? = currentTarget,
|
||||||
|
logMessage: String? = null,
|
||||||
|
showSnackMessage: String? = null,
|
||||||
|
) {
|
||||||
|
withContext(adbWorkerDispatcher) {
|
||||||
|
runCatching { nativeCore.scrcpyStop() }
|
||||||
|
runCatching { nativeCore.adbDisconnect() }
|
||||||
|
}
|
||||||
|
adbConnected = false
|
||||||
|
currentTargetHost = ""
|
||||||
|
currentTargetPort = AppDefaults.ADB_PORT
|
||||||
|
audioForwardingSupported = true
|
||||||
|
cameraMirroringSupported = true
|
||||||
|
sessionInfo = null
|
||||||
|
statusLine = "未连接"
|
||||||
|
connectedDeviceLabel = "未连接"
|
||||||
|
clearQuickOnlineForTarget?.let { target ->
|
||||||
|
if (target.host.isNotBlank()) {
|
||||||
|
upsertQuickDevice(context, quickDevices, target.host, target.port, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logMessage?.let { logEvent(it) }
|
||||||
|
if (!showSnackMessage.isNullOrBlank()) {
|
||||||
|
scope.launch {
|
||||||
|
snack.showSnackbar(showSnackMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun disconnectCurrentTargetBeforeConnecting(newHost: String, newPort: Int) {
|
||||||
|
val current = currentTarget
|
||||||
|
if (!adbConnected || current == null) return
|
||||||
|
if (current.host == newHost && current.port == newPort) return
|
||||||
|
|
||||||
|
sessionReconnectBlacklistHosts += current.host
|
||||||
|
logEvent("切换连接目标,先断开当前设备: ${current.host}:${current.port}")
|
||||||
|
disconnectAdbConnection(clearQuickOnlineForTarget = current)
|
||||||
|
}
|
||||||
|
|
||||||
fun applyConnectedDeviceCapabilities(sdkInt: Int, release: String) {
|
fun applyConnectedDeviceCapabilities(sdkInt: Int, release: String) {
|
||||||
val audioSupported = sdkInt !in 0..<30
|
val audioSupported = sdkInt !in 0..<30
|
||||||
audioForwardingSupported = audioSupported
|
audioForwardingSupported = audioSupported
|
||||||
@@ -337,7 +386,7 @@ fun DeviceTabScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun connectWithTimeout(host: String, port: Int): Boolean {
|
suspend fun connectWithTimeout(host: String, port: Int): Boolean {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(adbWorkerDispatcher) {
|
||||||
withTimeout(ADB_CONNECT_TIMEOUT_MS) {
|
withTimeout(ADB_CONNECT_TIMEOUT_MS) {
|
||||||
nativeCore.adbConnect(host, port)
|
nativeCore.adbConnect(host, port)
|
||||||
}
|
}
|
||||||
@@ -345,7 +394,7 @@ fun DeviceTabScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun keepAliveCheck(host: String, port: Int): Boolean {
|
suspend fun keepAliveCheck(host: String, port: Int): Boolean {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(adbWorkerDispatcher) {
|
||||||
withTimeout(ADB_KEEPALIVE_TIMEOUT_MS) {
|
withTimeout(ADB_KEEPALIVE_TIMEOUT_MS) {
|
||||||
val connected = nativeCore.adbIsConnected()
|
val connected = nativeCore.adbIsConnected()
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
@@ -381,7 +430,9 @@ fun DeviceTabScreen(
|
|||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
val detail = e.message?.takeIf { it.isNotBlank() } ?: e.javaClass.simpleName
|
val detail = e.message?.takeIf { it.isNotBlank() } ?: e.javaClass.simpleName
|
||||||
logEvent("$label 参数错误: $detail", Log.WARN, e)
|
logEvent("$label 参数错误: $detail", Log.WARN, e)
|
||||||
snack.showSnackbar("$label 参数错误: $detail")
|
scope.launch {
|
||||||
|
snack.showSnackbar("$label 参数错误: $detail")
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
val detail = e.message?.takeIf { it.isNotBlank() } ?: e.javaClass.simpleName
|
val detail = e.message?.takeIf { it.isNotBlank() } ?: e.javaClass.simpleName
|
||||||
logEvent("$label 失败: $detail", Log.ERROR, e)
|
logEvent("$label 失败: $detail", Log.ERROR, e)
|
||||||
@@ -403,7 +454,9 @@ fun DeviceTabScreen(
|
|||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
val detail = e.message?.takeIf { it.isNotBlank() } ?: e.javaClass.simpleName
|
val detail = e.message?.takeIf { it.isNotBlank() } ?: e.javaClass.simpleName
|
||||||
logEvent("$label 参数错误: $detail", Log.WARN, e)
|
logEvent("$label 参数错误: $detail", Log.WARN, e)
|
||||||
snack.showSnackbar("$label 参数错误: $detail")
|
scope.launch {
|
||||||
|
snack.showSnackbar("$label 参数错误: $detail")
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
val detail = e.message?.takeIf { it.isNotBlank() } ?: e.javaClass.simpleName
|
val detail = e.message?.takeIf { it.isNotBlank() } ?: e.javaClass.simpleName
|
||||||
logEvent("$label 失败: $detail", Log.ERROR, e)
|
logEvent("$label 失败: $detail", Log.ERROR, e)
|
||||||
@@ -414,6 +467,16 @@ fun DeviceTabScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun runAutoAdbConnect(host: String, port: Int): Boolean {
|
||||||
|
return runCatching {
|
||||||
|
connectWithTimeout(host, port)
|
||||||
|
}.getOrElse { error ->
|
||||||
|
val detail = error.message?.takeIf { it.isNotBlank() } ?: error.javaClass.simpleName
|
||||||
|
logEvent("自动重连失败: $host:$port ($detail)", Log.WARN)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun refreshEncoderLists() {
|
fun refreshEncoderLists() {
|
||||||
if (!adbConnected) return
|
if (!adbConnected) return
|
||||||
val remotePath = serverRemotePath.trim().ifBlank { AppDefaults.SERVER_REMOTE_PATH }
|
val remotePath = serverRemotePath.trim().ifBlank { AppDefaults.SERVER_REMOTE_PATH }
|
||||||
@@ -627,10 +690,8 @@ fun DeviceTabScreen(
|
|||||||
snack.showSnackbar("ADB 自动重连成功")
|
snack.showSnackbar("ADB 自动重连成功")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
runCatching { nativeCore.adbDisconnect() }
|
disconnectAdbConnection()
|
||||||
statusLine = "ADB 连接断开"
|
statusLine = "ADB 连接断开"
|
||||||
connectedDeviceLabel = "未连接"
|
|
||||||
sessionInfo = null
|
|
||||||
logEvent("ADB 自动重连失败: $host:$port", Log.ERROR)
|
logEvent("ADB 自动重连失败: $host:$port", Log.ERROR)
|
||||||
scope.launch {
|
scope.launch {
|
||||||
snack.showSnackbar("ADB 自动重连失败")
|
snack.showSnackbar("ADB 自动重连失败")
|
||||||
@@ -662,20 +723,19 @@ fun DeviceTabScreen(
|
|||||||
if (!portReachable) continue
|
if (!portReachable) continue
|
||||||
|
|
||||||
quickConnectTriedOnce += targetKey
|
quickConnectTriedOnce += targetKey
|
||||||
runAdbConnect("快速设备端口可达,尝试连接一次") {
|
val ok = runAutoAdbConnect(target.host, target.port)
|
||||||
val ok = connectWithTimeout(target.host, target.port)
|
adbConnected = ok
|
||||||
adbConnected = ok
|
upsertQuickDevice(
|
||||||
upsertQuickDevice(
|
context,
|
||||||
context,
|
quickDevices,
|
||||||
quickDevices,
|
target.host,
|
||||||
target.host,
|
target.port,
|
||||||
target.port,
|
ok
|
||||||
ok
|
)
|
||||||
)
|
if (ok) {
|
||||||
if (ok) {
|
handleAdbConnected(target.host, target.port)
|
||||||
handleAdbConnected(target.host, target.port)
|
logEvent("ADB 快速探测连接成功: ${target.host}:${target.port}")
|
||||||
logEvent("ADB 快速探测连接成功: ${target.host}:${target.port}")
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (adbConnected) break
|
if (adbConnected) break
|
||||||
@@ -727,22 +787,20 @@ fun DeviceTabScreen(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
runAdbConnect("自动重连 ADB") {
|
val ok = runAutoAdbConnect(discoveredHost, discoveredPort)
|
||||||
val ok = connectWithTimeout(discoveredHost, discoveredPort)
|
adbConnected = ok
|
||||||
adbConnected = ok
|
upsertQuickDevice(
|
||||||
upsertQuickDevice(
|
context,
|
||||||
context,
|
quickDevices,
|
||||||
quickDevices,
|
discoveredHost,
|
||||||
discoveredHost,
|
discoveredPort,
|
||||||
discoveredPort,
|
ok
|
||||||
ok
|
)
|
||||||
)
|
if (ok) {
|
||||||
if (ok) {
|
handleAdbConnected(discoveredHost, discoveredPort)
|
||||||
handleAdbConnected(discoveredHost, discoveredPort)
|
logEvent("ADB 自动重连成功: $discoveredHost:$discoveredPort")
|
||||||
logEvent("ADB 自动重连成功: $discoveredHost:$discoveredPort")
|
} else {
|
||||||
} else {
|
logEvent("ADB 自动重连失败: $discoveredHost:$discoveredPort", Log.WARN)
|
||||||
logEvent("ADB 自动重连失败: $discoveredHost:$discoveredPort", Log.WARN)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(ADB_AUTO_RECONNECT_RETRY_INTERVAL_MS)
|
delay(ADB_AUTO_RECONNECT_RETRY_INTERVAL_MS)
|
||||||
@@ -759,19 +817,6 @@ fun DeviceTabScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DisposableEffect(activity, keepScreenOnWhenStreamingEnabled, sessionInfo != null) {
|
|
||||||
val window = activity?.window
|
|
||||||
val shouldKeepScreenOn = keepScreenOnWhenStreamingEnabled && sessionInfo != null
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(sessionInfo) {
|
LaunchedEffect(sessionInfo) {
|
||||||
if (sessionInfo != null) {
|
if (sessionInfo != null) {
|
||||||
sessionInfoWidth = sessionInfo?.width ?: 0
|
sessionInfoWidth = sessionInfo?.width ?: 0
|
||||||
@@ -808,7 +853,6 @@ fun DeviceTabScreen(
|
|||||||
onClearLogsActionChange(null)
|
onClearLogsActionChange(null)
|
||||||
onCanClearLogsChange(false)
|
onCanClearLogsChange(false)
|
||||||
onOpenReorderDevicesActionChange(null)
|
onOpenReorderDevicesActionChange(null)
|
||||||
onSessionStartedChange(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,26 +959,18 @@ fun DeviceTabScreen(
|
|||||||
haptics.press()
|
haptics.press()
|
||||||
if (isConnectedTarget) {
|
if (isConnectedTarget) {
|
||||||
activeDeviceActionId = device.id
|
activeDeviceActionId = device.id
|
||||||
runBusy("断开 ADB", onFinished = { activeDeviceActionId = null }) {
|
runAdbConnect("断开 ADB", onFinished = { activeDeviceActionId = null }) {
|
||||||
nativeCore.adbDisconnect()
|
|
||||||
sessionReconnectBlacklistHosts += host
|
sessionReconnectBlacklistHosts += host
|
||||||
adbConnected = false
|
disconnectAdbConnection(
|
||||||
currentTargetHost = ""
|
clearQuickOnlineForTarget = ConnectionTarget(host, port),
|
||||||
currentTargetPort = AppDefaults.ADB_PORT
|
logMessage = "ADB 已断开: ${device.name}",
|
||||||
audioForwardingSupported = true
|
showSnackMessage = "ADB 已断开",
|
||||||
cameraMirroringSupported = true
|
)
|
||||||
sessionInfo = null
|
|
||||||
statusLine = "未连接"
|
|
||||||
connectedDeviceLabel = "未连接"
|
|
||||||
upsertQuickDevice(context, quickDevices, host, port, false)
|
|
||||||
logEvent("ADB 已断开: ${device.name}")
|
|
||||||
scope.launch {
|
|
||||||
snack.showSnackbar("ADB 已断开")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
activeDeviceActionId = device.id
|
activeDeviceActionId = device.id
|
||||||
runAdbConnect("连接 ADB", onFinished = { activeDeviceActionId = null }) {
|
runAdbConnect("连接 ADB", onFinished = { activeDeviceActionId = null }) {
|
||||||
|
disconnectCurrentTargetBeforeConnecting(host, port)
|
||||||
val ok = connectWithTimeout(host, port)
|
val ok = connectWithTimeout(host, port)
|
||||||
adbConnected = ok
|
adbConnected = ok
|
||||||
upsertQuickDevice(context, quickDevices, host, port, ok)
|
upsertQuickDevice(context, quickDevices, host, port, ok)
|
||||||
@@ -975,6 +1011,7 @@ fun DeviceTabScreen(
|
|||||||
onConnect = {
|
onConnect = {
|
||||||
val target = parseQuickTarget(quickConnectInput) ?: return@QuickConnectCard
|
val target = parseQuickTarget(quickConnectInput) ?: return@QuickConnectCard
|
||||||
runAdbConnect("连接 ADB") {
|
runAdbConnect("连接 ADB") {
|
||||||
|
disconnectCurrentTargetBeforeConnecting(target.host, target.port)
|
||||||
val ok = connectWithTimeout(target.host, target.port)
|
val ok = connectWithTimeout(target.host, target.port)
|
||||||
adbConnected = ok
|
adbConnected = ok
|
||||||
upsertQuickDevice(context, quickDevices, target.host, target.port, ok)
|
upsertQuickDevice(context, quickDevices, target.host, target.port, ok)
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import android.app.Activity
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ActivityInfo
|
import android.content.pm.ActivityInfo
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.activity.compose.PredictiveBackHandler
|
import androidx.activity.compose.PredictiveBackHandler
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
@@ -23,6 +27,7 @@ import androidx.compose.runtime.DisposableEffect
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableLongStateOf
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateMapOf
|
import androidx.compose.runtime.mutableStateMapOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -51,7 +56,9 @@ import io.github.miuzarte.scrcpyforandroid.services.loadMainSettings
|
|||||||
import io.github.miuzarte.scrcpyforandroid.services.saveMainSettings
|
import io.github.miuzarte.scrcpyforandroid.services.saveMainSettings
|
||||||
import io.github.miuzarte.scrcpyforandroid.widgets.VirtualButtonActions
|
import io.github.miuzarte.scrcpyforandroid.widgets.VirtualButtonActions
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import top.yukonga.miuix.kmp.basic.DropdownImpl
|
import top.yukonga.miuix.kmp.basic.DropdownImpl
|
||||||
import top.yukonga.miuix.kmp.basic.Icon
|
import top.yukonga.miuix.kmp.basic.Icon
|
||||||
import top.yukonga.miuix.kmp.basic.IconButton
|
import top.yukonga.miuix.kmp.basic.IconButton
|
||||||
@@ -189,6 +196,7 @@ fun MainPage() {
|
|||||||
var openReorderDevicesAction by remember { mutableStateOf<(() -> Unit)?>(null) }
|
var openReorderDevicesAction by remember { mutableStateOf<(() -> Unit)?>(null) }
|
||||||
var canClearLogs by remember { mutableStateOf(false) }
|
var canClearLogs by remember { mutableStateOf(false) }
|
||||||
var showDeviceMenu by rememberSaveable { mutableStateOf(false) }
|
var showDeviceMenu by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var lastExitBackPressAtMs by rememberSaveable { mutableLongStateOf(0L) }
|
||||||
var fullscreenOrientation by rememberSaveable {
|
var fullscreenOrientation by rememberSaveable {
|
||||||
mutableIntStateOf(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
|
mutableIntStateOf(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
|
||||||
}
|
}
|
||||||
@@ -201,6 +209,19 @@ fun MainPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(activity, currentRootScreen, fullscreenOrientation) {
|
LaunchedEffect(activity, currentRootScreen, fullscreenOrientation) {
|
||||||
val targetOrientation = when (currentRootScreen) {
|
val targetOrientation = when (currentRootScreen) {
|
||||||
is RootScreen.Fullscreen -> fullscreenOrientation
|
is RootScreen.Fullscreen -> fullscreenOrientation
|
||||||
@@ -275,13 +296,34 @@ fun MainPage() {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
val now = SystemClock.elapsedRealtime()
|
||||||
|
if (now - lastExitBackPressAtMs > 2_000L) {
|
||||||
|
lastExitBackPressAtMs = now
|
||||||
|
Toast.makeText(context, "再按一次返回键退出", Toast.LENGTH_SHORT).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastExitBackPressAtMs = 0L
|
||||||
|
scope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
runCatching { nativeCore.scrcpyStop() }
|
||||||
|
runCatching { nativeCore.adbDisconnect() }
|
||||||
|
}
|
||||||
|
activity?.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val canNavigateBack = rootBackStack.size > 1 ||
|
val canNavigateBack = rootBackStack.size > 1 ||
|
||||||
pagerState.currentPage != MainTabDestination.Device.ordinal
|
pagerState.currentPage != MainTabDestination.Device.ordinal
|
||||||
|
|
||||||
PredictiveBackHandler(enabled = canNavigateBack) { progress ->
|
BackHandler(enabled = currentRootScreen !is RootScreen.Fullscreen) {
|
||||||
|
handleBackNavigation()
|
||||||
|
}
|
||||||
|
|
||||||
|
PredictiveBackHandler(
|
||||||
|
enabled = canNavigateBack && currentRootScreen !is RootScreen.Fullscreen
|
||||||
|
) { progress ->
|
||||||
try {
|
try {
|
||||||
progress.collect { }
|
progress.collect { }
|
||||||
handleBackNavigation()
|
handleBackNavigation()
|
||||||
@@ -380,7 +422,6 @@ fun MainPage() {
|
|||||||
nativeCore = nativeCore,
|
nativeCore = nativeCore,
|
||||||
snack = snackHostState,
|
snack = snackHostState,
|
||||||
scrollBehavior = deviceScrollBehavior,
|
scrollBehavior = deviceScrollBehavior,
|
||||||
keepScreenOnWhenStreamingEnabled = keepScreenOnWhenStreamingEnabled,
|
|
||||||
virtualButtonsOutside = virtualButtonsOutside,
|
virtualButtonsOutside = virtualButtonsOutside,
|
||||||
virtualButtonsInMore = virtualButtonsInMore,
|
virtualButtonsInMore = virtualButtonsInMore,
|
||||||
customServerUri = customServerUri,
|
customServerUri = customServerUri,
|
||||||
|
|||||||
Reference in New Issue
Block a user