refactor: navigation and haptic move into composition locals
This commit is contained in:
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
|
|
||||||
@@ -29,6 +30,10 @@ data class AppHaptics(
|
|||||||
val virtualKey: () -> Unit,
|
val virtualKey: () -> Unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val LocalAppHaptics = staticCompositionLocalOf<AppHaptics> {
|
||||||
|
error("No AppHaptics provided")
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberAppHaptics(): AppHaptics {
|
fun rememberAppHaptics(): AppHaptics {
|
||||||
val hapticFeedback = LocalHapticFeedback.current
|
val hapticFeedback = LocalHapticFeedback.current
|
||||||
|
|||||||
@@ -74,9 +74,8 @@ import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
|
|||||||
import androidx.compose.ui.graphics.BlendMode as ComposeBlendMode
|
import androidx.compose.ui.graphics.BlendMode as ComposeBlendMode
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun AboutScreen(
|
internal fun AboutScreen() {
|
||||||
onBack: () -> Unit,
|
val navigator = LocalRootNavigator.current
|
||||||
) {
|
|
||||||
val topAppBarScrollBehavior = MiuixScrollBehavior()
|
val topAppBarScrollBehavior = MiuixScrollBehavior()
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
var logoHeightPx by remember { mutableIntStateOf(0) }
|
var logoHeightPx by remember { mutableIntStateOf(0) }
|
||||||
@@ -102,9 +101,9 @@ internal fun AboutScreen(
|
|||||||
titleColor = colorScheme.onSurface.copy(alpha = scrollProgress),
|
titleColor = colorScheme.onSurface.copy(alpha = scrollProgress),
|
||||||
defaultWindowInsetsPadding = false,
|
defaultWindowInsetsPadding = false,
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onBack) {
|
IconButton(onClick = navigator.pop) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
|
Icons.AutoMirrored.Rounded.ArrowBack,
|
||||||
contentDescription = "返回",
|
contentDescription = "返回",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package io.github.miuzarte.scrcpyforandroid.pages
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
@@ -28,7 +29,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade
|
import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.Defaults
|
import io.github.miuzarte.scrcpyforandroid.constants.Defaults
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
||||||
import io.github.miuzarte.scrcpyforandroid.haptics.rememberAppHaptics
|
import io.github.miuzarte.scrcpyforandroid.haptics.LocalAppHaptics
|
||||||
import io.github.miuzarte.scrcpyforandroid.models.ConnectionTarget
|
import io.github.miuzarte.scrcpyforandroid.models.ConnectionTarget
|
||||||
import io.github.miuzarte.scrcpyforandroid.models.DeviceShortcut
|
import io.github.miuzarte.scrcpyforandroid.models.DeviceShortcut
|
||||||
import io.github.miuzarte.scrcpyforandroid.models.DeviceShortcuts
|
import io.github.miuzarte.scrcpyforandroid.models.DeviceShortcuts
|
||||||
@@ -37,7 +38,7 @@ 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.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.LocalSnackbarController
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.fetchConnectedDeviceInfo
|
import io.github.miuzarte.scrcpyforandroid.services.fetchConnectedDeviceInfo
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings
|
import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings
|
||||||
@@ -90,13 +91,10 @@ fun DeviceTabScreen(
|
|||||||
nativeCore: NativeCoreFacade,
|
nativeCore: NativeCoreFacade,
|
||||||
adbService: NativeAdbService,
|
adbService: NativeAdbService,
|
||||||
scrcpy: Scrcpy,
|
scrcpy: Scrcpy,
|
||||||
snackbar: SnackbarController,
|
|
||||||
scrollBehavior: ScrollBehavior,
|
scrollBehavior: ScrollBehavior,
|
||||||
onOpenVirtualButtonOrder: () -> Unit,
|
|
||||||
onOpenReorderDevices: () -> Unit,
|
onOpenReorderDevices: () -> Unit,
|
||||||
onOpenAdvancedPage: () -> Unit,
|
|
||||||
onOpenFullscreenPage: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
|
val navigator = LocalRootNavigator.current
|
||||||
var showThreePointMenu by rememberSaveable { mutableStateOf(false) }
|
var showThreePointMenu by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -121,7 +119,7 @@ fun DeviceTabScreen(
|
|||||||
showThreePointMenu = false
|
showThreePointMenu = false
|
||||||
},
|
},
|
||||||
onOpenVirtualButtonOrder = {
|
onOpenVirtualButtonOrder = {
|
||||||
onOpenVirtualButtonOrder()
|
navigator.push(RootScreen.VirtualButtonOrder)
|
||||||
showThreePointMenu = false
|
showThreePointMenu = false
|
||||||
},
|
},
|
||||||
canClearLogs = EventLogger.hasLogs(),
|
canClearLogs = EventLogger.hasLogs(),
|
||||||
@@ -140,10 +138,7 @@ fun DeviceTabScreen(
|
|||||||
nativeCore = nativeCore,
|
nativeCore = nativeCore,
|
||||||
adbService = adbService,
|
adbService = adbService,
|
||||||
scrcpy = scrcpy,
|
scrcpy = scrcpy,
|
||||||
snackbar = snackbar,
|
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
onOpenAdvancedPage = onOpenAdvancedPage,
|
|
||||||
onOpenFullscreenPage = onOpenFullscreenPage,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,17 +149,18 @@ fun DeviceTabPage(
|
|||||||
nativeCore: NativeCoreFacade,
|
nativeCore: NativeCoreFacade,
|
||||||
adbService: NativeAdbService,
|
adbService: NativeAdbService,
|
||||||
scrcpy: Scrcpy,
|
scrcpy: Scrcpy,
|
||||||
snackbar: SnackbarController,
|
|
||||||
scrollBehavior: ScrollBehavior,
|
scrollBehavior: ScrollBehavior,
|
||||||
onOpenAdvancedPage: () -> Unit,
|
|
||||||
onOpenFullscreenPage: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val taskScope = remember { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
|
val taskScope = remember { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
|
||||||
val haptics = rememberAppHaptics()
|
|
||||||
val activity = remember(context) { context as? Activity }
|
val activity = remember(context) { context as? Activity }
|
||||||
|
|
||||||
|
val haptics = LocalAppHaptics.current
|
||||||
|
val navigator = LocalRootNavigator.current
|
||||||
|
val fullscreenNavigationState = LocalFullscreenNavigationState.current
|
||||||
|
val snackbar = LocalSnackbarController.current
|
||||||
|
|
||||||
val asBundleShared by appSettings.bundleState.collectAsState()
|
val asBundleShared by appSettings.bundleState.collectAsState()
|
||||||
val asBundleSharedLatest by rememberUpdatedState(asBundleShared)
|
val asBundleSharedLatest by rememberUpdatedState(asBundleShared)
|
||||||
var asBundle by rememberSaveable(asBundleShared) { mutableStateOf(asBundleShared) }
|
var asBundle by rememberSaveable(asBundleShared) { mutableStateOf(asBundleShared) }
|
||||||
@@ -869,12 +865,11 @@ fun DeviceTabPage(
|
|||||||
SectionSmallTitle("Scrcpy")
|
SectionSmallTitle("Scrcpy")
|
||||||
ConfigPanel(
|
ConfigPanel(
|
||||||
busy = busy,
|
busy = busy,
|
||||||
snackbar = snackbar,
|
|
||||||
audioForwardingSupported = audioForwardingSupported,
|
audioForwardingSupported = audioForwardingSupported,
|
||||||
cameraMirroringSupported = cameraMirroringSupported,
|
cameraMirroringSupported = cameraMirroringSupported,
|
||||||
adbConnecting = adbConnecting,
|
adbConnecting = adbConnecting,
|
||||||
isQuickConnected = isQuickConnected,
|
isQuickConnected = isQuickConnected,
|
||||||
onOpenAdvanced = onOpenAdvancedPage,
|
onOpenAdvanced = { navigator.push(RootScreen.Advanced) },
|
||||||
onStartStopHaptic = { haptics.contextClick() },
|
onStartStopHaptic = { haptics.contextClick() },
|
||||||
onStart = {
|
onStart = {
|
||||||
runBusy("启动 scrcpy") {
|
runBusy("启动 scrcpy") {
|
||||||
@@ -949,8 +944,16 @@ fun DeviceTabPage(
|
|||||||
previewControlsVisible = !previewControlsVisible
|
previewControlsVisible = !previewControlsVisible
|
||||||
},
|
},
|
||||||
onOpenFullscreen = {
|
onOpenFullscreen = {
|
||||||
if (sessionInfo != null) {
|
val currentSession = sessionInfo
|
||||||
onOpenFullscreenPage()
|
if (currentSession != null) {
|
||||||
|
fullscreenNavigationState.setOrientation(
|
||||||
|
if (currentSession.width >= currentSession.height) {
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||||
|
} else {
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||||
|
}
|
||||||
|
)
|
||||||
|
navigator.push(RootScreen.Fullscreen)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import androidx.core.view.WindowCompat
|
|||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade
|
import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade
|
||||||
import io.github.miuzarte.scrcpyforandroid.haptics.rememberAppHaptics
|
import io.github.miuzarte.scrcpyforandroid.haptics.LocalAppHaptics
|
||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings
|
import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings
|
||||||
@@ -42,16 +42,16 @@ import top.yukonga.miuix.kmp.basic.Scaffold
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun FullscreenControlScreen(
|
fun FullscreenControlScreen(
|
||||||
onBack: () -> Unit,
|
|
||||||
scrcpy: Scrcpy,
|
scrcpy: Scrcpy,
|
||||||
nativeCore: NativeCoreFacade,
|
nativeCore: NativeCoreFacade,
|
||||||
onVideoSizeChanged: (width: Int, height: Int) -> Unit,
|
onVideoSizeChanged: (width: Int, height: Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
BackHandler(enabled = true, onBack = onBack)
|
val navigator = LocalRootNavigator.current
|
||||||
|
BackHandler(enabled = true, onBack = navigator.pop)
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
val haptics = rememberAppHaptics()
|
val haptics = LocalAppHaptics.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ fun FullscreenControlScreen(
|
|||||||
FullscreenControlScreen(
|
FullscreenControlScreen(
|
||||||
session = session,
|
session = session,
|
||||||
nativeCore = nativeCore,
|
nativeCore = nativeCore,
|
||||||
onDismiss = onBack,
|
onDismiss = navigator.pop,
|
||||||
showDebugInfo = fullscreenDebugInfo,
|
showDebugInfo = fullscreenDebugInfo,
|
||||||
currentFps = currentFps,
|
currentFps = currentFps,
|
||||||
enableBackHandler = false,
|
enableBackHandler = false,
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package io.github.miuzarte.scrcpyforandroid.pages
|
||||||
|
|
||||||
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
|
|
||||||
|
class ServerPicker(
|
||||||
|
val pick: () -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
|
val LocalServerPicker = staticCompositionLocalOf<ServerPicker> {
|
||||||
|
error("No ServerPicker provided")
|
||||||
|
}
|
||||||
@@ -11,16 +11,15 @@ 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
|
||||||
import androidx.compose.animation.core.spring
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.PagerState
|
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Devices
|
import androidx.compose.material.icons.rounded.Devices
|
||||||
import androidx.compose.material.icons.rounded.Settings
|
import androidx.compose.material.icons.rounded.Settings
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.DisposableEffect
|
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
|
||||||
@@ -46,10 +45,12 @@ import io.github.miuzarte.scrcpyforandroid.BuildConfig
|
|||||||
import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade
|
import io.github.miuzarte.scrcpyforandroid.NativeCoreFacade
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.ThemeModes
|
import io.github.miuzarte.scrcpyforandroid.constants.ThemeModes
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.UiMotion
|
import io.github.miuzarte.scrcpyforandroid.constants.UiMotion
|
||||||
import io.github.miuzarte.scrcpyforandroid.models.DeviceShortcuts
|
import io.github.miuzarte.scrcpyforandroid.haptics.LocalAppHaptics
|
||||||
|
import io.github.miuzarte.scrcpyforandroid.haptics.rememberAppHaptics
|
||||||
import io.github.miuzarte.scrcpyforandroid.nativecore.NativeAdbService
|
import io.github.miuzarte.scrcpyforandroid.nativecore.NativeAdbService
|
||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.AppUpdateChecker
|
import io.github.miuzarte.scrcpyforandroid.services.AppUpdateChecker
|
||||||
|
import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
||||||
@@ -90,21 +91,19 @@ sealed interface RootScreen : NavKey {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen() {
|
fun MainScreen() {
|
||||||
|
// Environment
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val appContext = context.applicationContext
|
val appContext = context.applicationContext
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
|
||||||
|
|
||||||
val activity = remember(context) { context as? Activity }
|
val activity = remember(context) { context as? Activity }
|
||||||
val initialOrientation = remember(activity) {
|
val initialOrientation = remember(activity) {
|
||||||
activity?.requestedOrientation ?: ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
activity?.requestedOrientation ?: ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
||||||
}
|
}
|
||||||
DisposableEffect(activity) {
|
|
||||||
onDispose {
|
|
||||||
activity?.requestedOrientation = initialOrientation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Scopes
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
||||||
|
|
||||||
|
// Core services
|
||||||
val nativeCore = remember(appContext) {
|
val nativeCore = remember(appContext) {
|
||||||
NativeCoreFacade.get(appContext)
|
NativeCoreFacade.get(appContext)
|
||||||
}
|
}
|
||||||
@@ -112,6 +111,7 @@ fun MainScreen() {
|
|||||||
NativeAdbService(appContext)
|
NativeAdbService(appContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global controllers provided to the compose tree
|
||||||
val snackHostState = remember { SnackbarHostState() }
|
val snackHostState = remember { SnackbarHostState() }
|
||||||
val snackbarController = remember(scope, snackHostState) {
|
val snackbarController = remember(scope, snackHostState) {
|
||||||
SnackbarController(
|
SnackbarController(
|
||||||
@@ -119,6 +119,9 @@ fun MainScreen() {
|
|||||||
hostState = snackHostState,
|
hostState = snackHostState,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val haptics = rememberAppHaptics()
|
||||||
|
|
||||||
|
// Root navigation and UI chrome state
|
||||||
val saveableStateHolder = rememberSaveableStateHolder()
|
val saveableStateHolder = rememberSaveableStateHolder()
|
||||||
val tabs = remember { MainBottomTabDestination.entries }
|
val tabs = remember { MainBottomTabDestination.entries }
|
||||||
val pagerState = rememberPagerState(
|
val pagerState = rememberPagerState(
|
||||||
@@ -127,6 +130,19 @@ fun MainScreen() {
|
|||||||
val currentTab = tabs[pagerState.currentPage]
|
val currentTab = tabs[pagerState.currentPage]
|
||||||
val rootBackStack = remember { mutableStateListOf<NavKey>(RootScreen.Home) }
|
val rootBackStack = remember { mutableStateListOf<NavKey>(RootScreen.Home) }
|
||||||
val currentRootScreen = rootBackStack.lastOrNull() as? RootScreen ?: RootScreen.Home
|
val currentRootScreen = rootBackStack.lastOrNull() as? RootScreen ?: RootScreen.Home
|
||||||
|
var showReorderDevices by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var fullscreenOrientation by rememberSaveable {
|
||||||
|
mutableIntStateOf(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
|
||||||
|
}
|
||||||
|
var lastExitBackPressAtMs by rememberSaveable { mutableLongStateOf(0L) }
|
||||||
|
|
||||||
|
DisposableEffect(activity) {
|
||||||
|
onDispose {
|
||||||
|
activity?.requestedOrientation = initialOrientation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll behaviors
|
||||||
val devicesPageScrollBehavior = MiuixScrollBehavior(
|
val devicesPageScrollBehavior = MiuixScrollBehavior(
|
||||||
canScroll = { currentTab == MainBottomTabDestination.Device })
|
canScroll = { currentTab == MainBottomTabDestination.Device })
|
||||||
val settingsPageScrollBehavior = MiuixScrollBehavior(
|
val settingsPageScrollBehavior = MiuixScrollBehavior(
|
||||||
@@ -140,17 +156,23 @@ fun MainScreen() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
fun popRoot() {
|
// Navigation helpers
|
||||||
if (rootBackStack.size > 1)
|
val rootNavigator = remember {
|
||||||
rootBackStack.removeAt(rootBackStack.lastIndex)
|
RootNavigator(
|
||||||
|
push = { rootBackStack.add(it) },
|
||||||
|
pop = {
|
||||||
|
if (rootBackStack.size > 1)
|
||||||
|
rootBackStack.removeAt(rootBackStack.lastIndex)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val fullscreenNavigationState = remember {
|
||||||
|
FullscreenNavigationState(
|
||||||
|
setOrientation = { fullscreenOrientation = it }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unified back behavior:
|
// Shared settings bundles
|
||||||
// 1) pop inner route
|
|
||||||
// 2) switch tab back to Device
|
|
||||||
// 3) double-back to exit and disconnect adb/scrcpy
|
|
||||||
var lastExitBackPressAtMs by rememberSaveable { mutableLongStateOf(0L) }
|
|
||||||
|
|
||||||
val asBundleShared by appSettings.bundleState.collectAsState()
|
val asBundleShared by appSettings.bundleState.collectAsState()
|
||||||
val asBundleSharedLatest by rememberUpdatedState(asBundleShared)
|
val asBundleSharedLatest by rememberUpdatedState(asBundleShared)
|
||||||
var asBundle by rememberSaveable(asBundleShared) { mutableStateOf(asBundleShared) }
|
var asBundle by rememberSaveable(asBundleShared) { mutableStateOf(asBundleShared) }
|
||||||
@@ -197,6 +219,7 @@ fun MainScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scrcpy instance and session state
|
||||||
val customServerUri = asBundle.customServerUri
|
val customServerUri = asBundle.customServerUri
|
||||||
.ifBlank { null }
|
.ifBlank { null }
|
||||||
val customServerVersion = asBundle.customServerVersion
|
val customServerVersion = asBundle.customServerVersion
|
||||||
@@ -220,6 +243,35 @@ fun MainScreen() {
|
|||||||
}
|
}
|
||||||
val currentSession by scrcpy.currentSessionState.collectAsState()
|
val currentSession by scrcpy.currentSessionState.collectAsState()
|
||||||
|
|
||||||
|
// Side-effect launchers and composition locals
|
||||||
|
val picker = rememberLauncherForActivityResult(
|
||||||
|
ActivityResultContracts.OpenDocument()
|
||||||
|
) { uri: Uri? ->
|
||||||
|
if (uri == null) return@rememberLauncherForActivityResult
|
||||||
|
runCatching {
|
||||||
|
context.contentResolver.takePersistableUriPermission(
|
||||||
|
uri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
asBundle = asBundle.copy(customServerUri = uri.toString())
|
||||||
|
}
|
||||||
|
val serverPicker = remember(picker) {
|
||||||
|
ServerPicker {
|
||||||
|
picker.launch(
|
||||||
|
arrayOf(
|
||||||
|
"application/java-archive",
|
||||||
|
"application/octet-stream",
|
||||||
|
"*/*"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derived flags
|
||||||
|
val canNavigateBack = rootBackStack.size > 1
|
||||||
|
|| pagerState.currentPage != MainBottomTabDestination.Device.ordinal
|
||||||
|
|
||||||
LaunchedEffect(asBundle.lastUpdateCheckAt) {
|
LaunchedEffect(asBundle.lastUpdateCheckAt) {
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (now - asBundle.lastUpdateCheckAt < AppUpdateChecker.CHECK_INTERVAL_MS) return@LaunchedEffect
|
if (now - asBundle.lastUpdateCheckAt < AppUpdateChecker.CHECK_INTERVAL_MS) return@LaunchedEffect
|
||||||
@@ -231,7 +283,7 @@ fun MainScreen() {
|
|||||||
|
|
||||||
fun handleBackNavigation() {
|
fun handleBackNavigation() {
|
||||||
if (rootBackStack.size > 1) {
|
if (rootBackStack.size > 1) {
|
||||||
popRoot()
|
rootNavigator.pop()
|
||||||
} else if (pagerState.currentPage != MainBottomTabDestination.Device.ordinal) {
|
} else if (pagerState.currentPage != MainBottomTabDestination.Device.ordinal) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
pagerState.animateScrollToPage(
|
pagerState.animateScrollToPage(
|
||||||
@@ -260,14 +312,6 @@ fun MainScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var showReorderDevices by rememberSaveable { mutableStateOf(false) }
|
|
||||||
var fullscreenOrientation by rememberSaveable {
|
|
||||||
mutableIntStateOf(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
|
|
||||||
}
|
|
||||||
|
|
||||||
val canNavigateBack = rootBackStack.size > 1
|
|
||||||
|| pagerState.currentPage != MainBottomTabDestination.Device.ordinal
|
|
||||||
|
|
||||||
BackHandler(enabled = currentRootScreen !is RootScreen.Fullscreen) {
|
BackHandler(enabled = currentRootScreen !is RootScreen.Fullscreen) {
|
||||||
handleBackNavigation()
|
handleBackNavigation()
|
||||||
}
|
}
|
||||||
@@ -317,18 +361,6 @@ fun MainScreen() {
|
|||||||
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 }
|
||||||
}
|
}
|
||||||
val picker = rememberLauncherForActivityResult(
|
|
||||||
ActivityResultContracts.OpenDocument()
|
|
||||||
) { uri: Uri? ->
|
|
||||||
if (uri == null) return@rememberLauncherForActivityResult
|
|
||||||
runCatching {
|
|
||||||
context.contentResolver.takePersistableUriPermission(
|
|
||||||
uri,
|
|
||||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
||||||
)
|
|
||||||
}
|
|
||||||
asBundle = asBundle.copy(customServerUri = uri.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
val rootEntryProvider = entryProvider<NavKey> {
|
val rootEntryProvider = entryProvider<NavKey> {
|
||||||
entry(RootScreen.Home) {
|
entry(RootScreen.Home) {
|
||||||
@@ -371,39 +403,13 @@ fun MainScreen() {
|
|||||||
nativeCore = nativeCore,
|
nativeCore = nativeCore,
|
||||||
adbService = adbService,
|
adbService = adbService,
|
||||||
scrcpy = scrcpy,
|
scrcpy = scrcpy,
|
||||||
snackbar = snackbarController,
|
|
||||||
scrollBehavior = devicesPageScrollBehavior,
|
scrollBehavior = devicesPageScrollBehavior,
|
||||||
onOpenVirtualButtonOrder = { rootBackStack.add(RootScreen.VirtualButtonOrder) },
|
|
||||||
onOpenReorderDevices = { showReorderDevices = true },
|
onOpenReorderDevices = { showReorderDevices = true },
|
||||||
onOpenAdvancedPage = { rootBackStack.add(RootScreen.Advanced) },
|
|
||||||
onOpenFullscreenPage = {
|
|
||||||
currentSession?.let { session ->
|
|
||||||
fullscreenOrientation =
|
|
||||||
if (session.width >= session.height) {
|
|
||||||
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
|
||||||
} else {
|
|
||||||
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
|
||||||
}
|
|
||||||
rootBackStack.add(RootScreen.Fullscreen)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
MainBottomTabDestination.Settings -> SettingsScreen(
|
MainBottomTabDestination.Settings -> SettingsScreen(
|
||||||
scrollBehavior = settingsPageScrollBehavior,
|
scrollBehavior = settingsPageScrollBehavior,
|
||||||
snackbar = snackbarController,
|
|
||||||
onOpenReorderDevices = { showReorderDevices = true },
|
onOpenReorderDevices = { showReorderDevices = true },
|
||||||
onOpenVirtualButtonOrder = { rootBackStack.add(RootScreen.VirtualButtonOrder) },
|
|
||||||
onOpenAbout = { rootBackStack.add(RootScreen.About) },
|
|
||||||
onPickServer = {
|
|
||||||
picker.launch(
|
|
||||||
arrayOf(
|
|
||||||
"application/java-archive",
|
|
||||||
"application/octet-stream",
|
|
||||||
"*/*"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -418,29 +424,23 @@ fun MainScreen() {
|
|||||||
|
|
||||||
entry(RootScreen.Advanced) {
|
entry(RootScreen.Advanced) {
|
||||||
ScrcpyAllOptionsScreen(
|
ScrcpyAllOptionsScreen(
|
||||||
onBack = ::popRoot,
|
|
||||||
scrollBehavior = advancedPageScrollBehavior,
|
scrollBehavior = advancedPageScrollBehavior,
|
||||||
snackbar = snackbarController,
|
|
||||||
scrcpy = scrcpy,
|
scrcpy = scrcpy,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry(RootScreen.About) {
|
entry(RootScreen.About) {
|
||||||
AboutScreen(
|
AboutScreen()
|
||||||
onBack = ::popRoot,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry(RootScreen.VirtualButtonOrder) {
|
entry(RootScreen.VirtualButtonOrder) {
|
||||||
VirtualButtonOrderScreen(
|
VirtualButtonOrderScreen(
|
||||||
onBack = ::popRoot,
|
|
||||||
scrollBehavior = advancedPageScrollBehavior,
|
scrollBehavior = advancedPageScrollBehavior,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry(RootScreen.Fullscreen) {
|
entry(RootScreen.Fullscreen) {
|
||||||
FullscreenControlScreen(
|
FullscreenControlScreen(
|
||||||
onBack = ::popRoot,
|
|
||||||
scrcpy = scrcpy,
|
scrcpy = scrcpy,
|
||||||
nativeCore = nativeCore,
|
nativeCore = nativeCore,
|
||||||
onVideoSizeChanged = { width, height ->
|
onVideoSizeChanged = { width, height ->
|
||||||
@@ -465,9 +465,17 @@ fun MainScreen() {
|
|||||||
val themeController = remember(themeMode) { ThemeController(colorSchemeMode = themeMode) }
|
val themeController = remember(themeMode) { ThemeController(colorSchemeMode = themeMode) }
|
||||||
|
|
||||||
MiuixTheme(controller = themeController) {
|
MiuixTheme(controller = themeController) {
|
||||||
NavDisplay(
|
CompositionLocalProvider(
|
||||||
entries = rootEntries,
|
LocalRootNavigator provides rootNavigator,
|
||||||
onBack = ::popRoot,
|
LocalSnackbarController provides snackbarController,
|
||||||
)
|
LocalAppHaptics provides haptics,
|
||||||
|
LocalFullscreenNavigationState provides fullscreenNavigationState,
|
||||||
|
LocalServerPicker provides serverPicker,
|
||||||
|
) {
|
||||||
|
NavDisplay(
|
||||||
|
entries = rootEntries,
|
||||||
|
onBack = rootNavigator.pop,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package io.github.miuzarte.scrcpyforandroid.pages
|
||||||
|
|
||||||
|
import android.content.pm.ActivityInfo
|
||||||
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
|
|
||||||
|
class RootNavigator(
|
||||||
|
val push: (RootScreen) -> Unit,
|
||||||
|
val pop: () -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
|
val LocalRootNavigator = staticCompositionLocalOf<RootNavigator> {
|
||||||
|
error("No RootNavigator provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
class FullscreenNavigationState(
|
||||||
|
val setOrientation: (Int) -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
|
val LocalFullscreenNavigationState = staticCompositionLocalOf<FullscreenNavigationState> {
|
||||||
|
error("No FullscreenNavigationState provided")
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@ import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperTextField
|
|||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Shared
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Shared
|
||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Shared.Codec
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Shared.Codec
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Storage.scrcpyOptions
|
import io.github.miuzarte.scrcpyforandroid.storage.Storage.scrcpyOptions
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -69,17 +69,17 @@ import kotlin.math.roundToInt
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ScrcpyAllOptionsScreen(
|
internal fun ScrcpyAllOptionsScreen(
|
||||||
onBack: () -> Unit,
|
|
||||||
scrollBehavior: ScrollBehavior,
|
scrollBehavior: ScrollBehavior,
|
||||||
snackbar: SnackbarController,
|
|
||||||
scrcpy: Scrcpy,
|
scrcpy: Scrcpy,
|
||||||
) {
|
) {
|
||||||
|
val snackbar = LocalSnackbarController.current
|
||||||
|
val navigator = LocalRootNavigator.current
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = "所有参数",
|
title = "所有参数",
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onBack) {
|
IconButton(onClick = navigator.pop) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.AutoMirrored.Rounded.ArrowBack,
|
Icons.AutoMirrored.Rounded.ArrowBack,
|
||||||
contentDescription = "返回"
|
contentDescription = "返回"
|
||||||
@@ -94,7 +94,6 @@ internal fun ScrcpyAllOptionsScreen(
|
|||||||
ScrcpyAllOptionsPage(
|
ScrcpyAllOptionsPage(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
snackbar = snackbar,
|
|
||||||
scrcpy = scrcpy,
|
scrcpy = scrcpy,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -104,12 +103,12 @@ internal fun ScrcpyAllOptionsScreen(
|
|||||||
internal fun ScrcpyAllOptionsPage(
|
internal fun ScrcpyAllOptionsPage(
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
scrollBehavior: ScrollBehavior,
|
scrollBehavior: ScrollBehavior,
|
||||||
snackbar: SnackbarController,
|
|
||||||
scrcpy: Scrcpy,
|
scrcpy: Scrcpy,
|
||||||
) {
|
) {
|
||||||
val focusManager = LocalFocusManager.current
|
val focusManager = LocalFocusManager.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val taskScope = remember { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
|
val taskScope = remember { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
|
||||||
|
val snackbar = LocalSnackbarController.current
|
||||||
|
|
||||||
var refreshBusy by rememberSaveable { mutableStateOf(false) }
|
var refreshBusy by rememberSaveable { mutableStateOf(false) }
|
||||||
var listRefreshVersion by rememberSaveable { mutableIntStateOf(0) }
|
var listRefreshVersion by rememberSaveable { mutableIntStateOf(0) }
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperSlider
|
|||||||
import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperTextField
|
import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperTextField
|
||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.AppUpdateChecker
|
import io.github.miuzarte.scrcpyforandroid.services.AppUpdateChecker
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.PreferenceMigration
|
import io.github.miuzarte.scrcpyforandroid.storage.PreferenceMigration
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
||||||
@@ -64,11 +64,7 @@ import kotlin.system.exitProcess
|
|||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
fun SettingsScreen(
|
||||||
scrollBehavior: ScrollBehavior,
|
scrollBehavior: ScrollBehavior,
|
||||||
snackbar: SnackbarController,
|
|
||||||
onOpenReorderDevices: () -> Unit,
|
onOpenReorderDevices: () -> Unit,
|
||||||
onOpenVirtualButtonOrder: () -> Unit,
|
|
||||||
onOpenAbout: () -> Unit,
|
|
||||||
onPickServer: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -81,11 +77,7 @@ fun SettingsScreen(
|
|||||||
SettingsPage(
|
SettingsPage(
|
||||||
contentPadding = pagePadding,
|
contentPadding = pagePadding,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
snackbar = snackbar,
|
|
||||||
onOpenReorderDevices = onOpenReorderDevices,
|
onOpenReorderDevices = onOpenReorderDevices,
|
||||||
onOpenVirtualButtonOrder = onOpenVirtualButtonOrder,
|
|
||||||
onOpenAbout = onOpenAbout,
|
|
||||||
onPickServer = onPickServer,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,11 +86,7 @@ fun SettingsScreen(
|
|||||||
fun SettingsPage(
|
fun SettingsPage(
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
scrollBehavior: ScrollBehavior,
|
scrollBehavior: ScrollBehavior,
|
||||||
snackbar: SnackbarController,
|
|
||||||
onOpenReorderDevices: () -> Unit,
|
onOpenReorderDevices: () -> Unit,
|
||||||
onOpenVirtualButtonOrder: () -> Unit,
|
|
||||||
onOpenAbout: () -> Unit,
|
|
||||||
onPickServer: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val appContext = context.applicationContext
|
val appContext = context.applicationContext
|
||||||
@@ -111,6 +99,10 @@ fun SettingsPage(
|
|||||||
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val snackbar = LocalSnackbarController.current
|
||||||
|
val navigator = LocalRootNavigator.current
|
||||||
|
val serverPicker = LocalServerPicker.current
|
||||||
|
|
||||||
val asBundleShared by appSettings.bundleState.collectAsState()
|
val asBundleShared by appSettings.bundleState.collectAsState()
|
||||||
val asBundleSharedLatest by rememberUpdatedState(asBundleShared)
|
val asBundleSharedLatest by rememberUpdatedState(asBundleShared)
|
||||||
var asBundle by rememberSaveable(asBundleShared) { mutableStateOf(asBundleShared) }
|
var asBundle by rememberSaveable(asBundleShared) { mutableStateOf(asBundleShared) }
|
||||||
@@ -257,7 +249,7 @@ fun SettingsPage(
|
|||||||
ArrowPreference(
|
ArrowPreference(
|
||||||
title = "虚拟按钮排序",
|
title = "虚拟按钮排序",
|
||||||
summary = "手动排序预览/全屏时的虚拟按钮,并选择哪些按钮展示在外",
|
summary = "手动排序预览/全屏时的虚拟按钮,并选择哪些按钮展示在外",
|
||||||
onClick = onOpenVirtualButtonOrder,
|
onClick = { navigator.push(RootScreen.VirtualButtonOrder) },
|
||||||
)
|
)
|
||||||
SwitchPreference(
|
SwitchPreference(
|
||||||
title = "全屏显示虚拟按钮",
|
title = "全屏显示虚拟按钮",
|
||||||
@@ -307,13 +299,13 @@ fun SettingsPage(
|
|||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Rounded.Clear,
|
Icons.Rounded.Clear,
|
||||||
contentDescription = "清空",
|
contentDescription = "清空",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(onClick = onPickServer) {
|
IconButton(onClick = serverPicker.pick) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Rounded.FileOpen,
|
Icons.Rounded.FileOpen,
|
||||||
contentDescription = "选择文件",
|
contentDescription = "选择文件",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -474,7 +466,7 @@ fun SettingsPage(
|
|||||||
ArrowPreference(
|
ArrowPreference(
|
||||||
title = "关于",
|
title = "关于",
|
||||||
summary = updateSummary,
|
summary = updateSummary,
|
||||||
onClick = onOpenAbout,
|
onClick = { navigator.push(RootScreen.About) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,16 +37,16 @@ import top.yukonga.miuix.kmp.preference.SwitchPreference
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun VirtualButtonOrderScreen(
|
internal fun VirtualButtonOrderScreen(
|
||||||
onBack: () -> Unit,
|
|
||||||
scrollBehavior: ScrollBehavior,
|
scrollBehavior: ScrollBehavior,
|
||||||
) {
|
) {
|
||||||
|
val navigator = LocalRootNavigator.current
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = "虚拟按钮排序",
|
title = "虚拟按钮排序",
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = onBack) {
|
IconButton(onClick = navigator.pop) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.AutoMirrored.Rounded.ArrowBack,
|
Icons.AutoMirrored.Rounded.ArrowBack,
|
||||||
contentDescription = "返回"
|
contentDescription = "返回"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.github.miuzarte.scrcpyforandroid.services
|
package io.github.miuzarte.scrcpyforandroid.services
|
||||||
|
|
||||||
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import top.yukonga.miuix.kmp.basic.SnackbarDuration
|
import top.yukonga.miuix.kmp.basic.SnackbarDuration
|
||||||
@@ -29,3 +30,7 @@ class SnackbarController(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val LocalSnackbarController = staticCompositionLocalOf<SnackbarController> {
|
||||||
|
error("No SnackbarController provided")
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperTextField
|
|||||||
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.scrcpy.Shared.Codec
|
||||||
import io.github.miuzarte.scrcpyforandroid.scrcpy.TouchEventHandler
|
import io.github.miuzarte.scrcpyforandroid.scrcpy.TouchEventHandler
|
||||||
import io.github.miuzarte.scrcpyforandroid.services.SnackbarController
|
import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
import io.github.miuzarte.scrcpyforandroid.storage.Settings
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Storage
|
import io.github.miuzarte.scrcpyforandroid.storage.Storage
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Storage.scrcpyOptions
|
import io.github.miuzarte.scrcpyforandroid.storage.Storage.scrcpyOptions
|
||||||
@@ -352,7 +352,6 @@ internal fun VirtualButtonCard(
|
|||||||
@Composable
|
@Composable
|
||||||
internal fun ConfigPanel(
|
internal fun ConfigPanel(
|
||||||
busy: Boolean,
|
busy: Boolean,
|
||||||
snackbar: SnackbarController,
|
|
||||||
audioForwardingSupported: Boolean,
|
audioForwardingSupported: Boolean,
|
||||||
cameraMirroringSupported: Boolean,
|
cameraMirroringSupported: Boolean,
|
||||||
adbConnecting: Boolean,
|
adbConnecting: Boolean,
|
||||||
@@ -366,6 +365,8 @@ internal fun ConfigPanel(
|
|||||||
) {
|
) {
|
||||||
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) }
|
||||||
|
|
||||||
|
val snackbar = LocalSnackbarController.current
|
||||||
|
|
||||||
val sessionStarted = sessionInfo != null
|
val sessionStarted = sessionInfo != null
|
||||||
|
|
||||||
val soBundleShared by scrcpyOptions.bundleState.collectAsState()
|
val soBundleShared by scrcpyOptions.bundleState.collectAsState()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
||||||
import io.github.miuzarte.scrcpyforandroid.haptics.rememberAppHaptics
|
import io.github.miuzarte.scrcpyforandroid.haptics.LocalAppHaptics
|
||||||
import sh.calvin.reorderable.ReorderableColumn
|
import sh.calvin.reorderable.ReorderableColumn
|
||||||
import sh.calvin.reorderable.ReorderableRow
|
import sh.calvin.reorderable.ReorderableRow
|
||||||
import top.yukonga.miuix.kmp.basic.Card
|
import top.yukonga.miuix.kmp.basic.Card
|
||||||
@@ -50,7 +50,7 @@ class ReorderableList(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
operator fun invoke() {
|
operator fun invoke() {
|
||||||
val haptics = rememberAppHaptics()
|
val haptics = LocalAppHaptics.current
|
||||||
val items = itemsProvider()
|
val items = itemsProvider()
|
||||||
when (orientation) {
|
when (orientation) {
|
||||||
Orientation.Column -> {
|
Orientation.Column -> {
|
||||||
@@ -81,7 +81,7 @@ class ReorderableList(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(UiSpacing.Small)
|
horizontalArrangement = Arrangement.spacedBy(UiSpacing.Small)
|
||||||
) {
|
) {
|
||||||
if (item.icon != null) Icon(
|
if (item.icon != null) Icon(
|
||||||
imageVector = item.icon,
|
item.icon,
|
||||||
contentDescription = item.title
|
contentDescription = item.title
|
||||||
)
|
)
|
||||||
Column {
|
Column {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
||||||
import io.github.miuzarte.scrcpyforandroid.haptics.rememberAppHaptics
|
import io.github.miuzarte.scrcpyforandroid.haptics.LocalAppHaptics
|
||||||
import top.yukonga.miuix.kmp.basic.Card
|
import top.yukonga.miuix.kmp.basic.Card
|
||||||
import top.yukonga.miuix.kmp.basic.CardDefaults.defaultColors
|
import top.yukonga.miuix.kmp.basic.CardDefaults.defaultColors
|
||||||
import top.yukonga.miuix.kmp.basic.Icon
|
import top.yukonga.miuix.kmp.basic.Icon
|
||||||
@@ -67,7 +67,7 @@ internal fun StatusCardLayout(
|
|||||||
spec: StatusCardSpec,
|
spec: StatusCardSpec,
|
||||||
busyLabel: String?,
|
busyLabel: String?,
|
||||||
) {
|
) {
|
||||||
val haptics = rememberAppHaptics()
|
val haptics = LocalAppHaptics.current
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -91,7 +91,7 @@ internal fun StatusCardLayout(
|
|||||||
contentAlignment = Alignment.BottomEnd,
|
contentAlignment = Alignment.BottomEnd,
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = spec.big.icon,
|
spec.big.icon,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(170.dp),
|
modifier = Modifier.size(170.dp),
|
||||||
tint = spec.big.iconTint,
|
tint = spec.big.iconTint,
|
||||||
@@ -153,7 +153,7 @@ internal fun StatusCardLayout(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun StatusMetricCard(spec: StatusSmallCardSpec, modifier: Modifier) {
|
private fun StatusMetricCard(spec: StatusSmallCardSpec, modifier: Modifier) {
|
||||||
val haptics = rememberAppHaptics()
|
val haptics = LocalAppHaptics.current
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
insideMargin = PaddingValues(UiSpacing.Large),
|
insideMargin = PaddingValues(UiSpacing.Large),
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.UiAndroidKeycodes
|
import io.github.miuzarte.scrcpyforandroid.constants.UiAndroidKeycodes
|
||||||
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing
|
||||||
import io.github.miuzarte.scrcpyforandroid.haptics.rememberAppHaptics
|
import io.github.miuzarte.scrcpyforandroid.haptics.LocalAppHaptics
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
import io.github.miuzarte.scrcpyforandroid.storage.AppSettings
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import top.yukonga.miuix.kmp.basic.Button
|
import top.yukonga.miuix.kmp.basic.Button
|
||||||
@@ -171,7 +171,7 @@ class VirtualButtonBar(
|
|||||||
onAction: (VirtualButtonAction) -> Unit,
|
onAction: (VirtualButtonAction) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val haptics = rememberAppHaptics()
|
val haptics = LocalAppHaptics.current
|
||||||
val activeContainerColor = MiuixTheme.colorScheme.primary
|
val activeContainerColor = MiuixTheme.colorScheme.primary
|
||||||
val disabledContainerColor = MiuixTheme.colorScheme.primary.copy(alpha = 0.35f)
|
val disabledContainerColor = MiuixTheme.colorScheme.primary.copy(alpha = 0.35f)
|
||||||
val activeContentColor = MiuixTheme.colorScheme.onPrimary
|
val activeContentColor = MiuixTheme.colorScheme.onPrimary
|
||||||
@@ -236,7 +236,7 @@ class VirtualButtonBar(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val haptics = rememberAppHaptics()
|
val haptics = LocalAppHaptics.current
|
||||||
var showMorePopup by remember { mutableStateOf(false) }
|
var showMorePopup by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
@@ -293,7 +293,7 @@ class VirtualButtonBar(
|
|||||||
renderInRootScaffold: Boolean,
|
renderInRootScaffold: Boolean,
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val haptics = rememberAppHaptics()
|
val haptics = LocalAppHaptics.current
|
||||||
val spinnerItems = remember(moreActions) {
|
val spinnerItems = remember(moreActions) {
|
||||||
moreActions.map { action ->
|
moreActions.map { action ->
|
||||||
SpinnerEntry(
|
SpinnerEntry(
|
||||||
|
|||||||
Reference in New Issue
Block a user