diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f8f87..ed57cce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 0.4.1 + +- 依赖: 同步 submodule/miuix +- 改进: 补全 haptic feedback +- 新增: 设备支持多个 IP, 自动选择延迟最低的路径 +- 新增: 主页允许调整卡片顺序 + ## 0.4.0 - 依赖: scrcpy 升级至 v4.0 diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/AboutScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/AboutScreen.kt index 426bf74..d938502 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/AboutScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/AboutScreen.kt @@ -2,32 +2,14 @@ package io.github.miuzarte.scrcpyforandroid.pages import android.content.Intent import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.BlendMode @@ -40,6 +22,7 @@ import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.positionInWindow import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -53,31 +36,20 @@ import io.github.miuzarte.scrcpyforandroid.R import io.github.miuzarte.scrcpyforandroid.pages.effect.BgEffectBackground import io.github.miuzarte.scrcpyforandroid.services.AppUpdateChecker import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur +import io.github.miuzarte.scrcpyforandroid.ui.contextClick import io.github.miuzarte.scrcpyforandroid.ui.rememberBlurBackdrop import kotlinx.coroutines.flow.onEach -import top.yukonga.miuix.kmp.basic.Card -import top.yukonga.miuix.kmp.basic.CardDefaults -import top.yukonga.miuix.kmp.basic.Icon -import top.yukonga.miuix.kmp.basic.IconButton -import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior -import top.yukonga.miuix.kmp.basic.Scaffold -import top.yukonga.miuix.kmp.basic.SmallTopAppBar -import top.yukonga.miuix.kmp.basic.Text -import top.yukonga.miuix.kmp.blur.BlendColorEntry -import top.yukonga.miuix.kmp.blur.BlurBlendMode -import top.yukonga.miuix.kmp.blur.BlurColors -import top.yukonga.miuix.kmp.blur.BlurDefaults -import top.yukonga.miuix.kmp.blur.isRenderEffectSupported -import top.yukonga.miuix.kmp.blur.layerBackdrop -import top.yukonga.miuix.kmp.blur.rememberLayerBackdrop -import top.yukonga.miuix.kmp.blur.textureBlur +import top.yukonga.miuix.kmp.basic.* +import top.yukonga.miuix.kmp.blur.* import top.yukonga.miuix.kmp.preference.ArrowPreference +import top.yukonga.miuix.kmp.shader.isRenderEffectSupported import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import top.yukonga.miuix.kmp.theme.MiuixTheme.textStyles import java.util.Locale.getDefault @Composable internal fun AboutScreen() { + val haptic = LocalHapticFeedback.current val navigator = LocalRootNavigator.current val enableBlur = LocalEnableBlur.current val blurBackdrop = rememberBlurBackdrop(enableBlur) @@ -115,7 +87,12 @@ internal fun AboutScreen() { titleColor = colorScheme.onSurface.copy(alpha = scrollProgress), defaultWindowInsetsPadding = false, navigationIcon = { - IconButton(onClick = navigator.pop) { + IconButton( + onClick = { + haptic.contextClick() + navigator.pop() + }, + ) { Icon( Icons.AutoMirrored.Rounded.ArrowBack, contentDescription = stringResource(R.string.cd_back), @@ -356,7 +333,7 @@ private fun AboutContent( detectTapGestures( onDoubleTap = { isOs3Effect = !isOs3Effect - } + }, ) } .onSizeChanged { onLogoHeightChanged(it.height) } @@ -387,7 +364,7 @@ private fun AboutContent( }, onClick = { context.startActivity( - Intent(Intent.ACTION_VIEW, AppUpdateChecker.REPO_URL.toUri()) + Intent(Intent.ACTION_VIEW, AppUpdateChecker.REPO_URL.toUri()), ) }, ) @@ -407,7 +384,7 @@ private fun AboutContent( Intent( Intent.ACTION_VIEW, releasesUrl.toUri(), - ) + ), ) }, ) @@ -427,7 +404,7 @@ private fun AboutContent( Intent( Intent.ACTION_VIEW, "https://www.apache.org/licenses/LICENSE-2.0.txt".toUri(), - ) + ), ) }, ) @@ -451,7 +428,7 @@ private fun AboutContent( Intent( Intent.ACTION_VIEW, repo.toUri(), - ) + ), ) }, ) diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/FileManagerScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/FileManagerScreen.kt index 41f3564..f3955df 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/FileManagerScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/FileManagerScreen.kt @@ -358,7 +358,7 @@ fun FileManagerScreen( } if (showPathDialog) { PathJumpDialog( - show = true, + show = showPathDialog, path = pathInput, onPathChange = { pathInput = it }, onDismissRequest = { showPathDialog = false }, @@ -370,7 +370,7 @@ fun FileManagerScreen( } if (showCreateFolderDialog) { CreateFolderDialog( - show = true, + show = showCreateFolderDialog, folderName = newFolderName, onFolderNameChange = { newFolderName = it }, onDismissRequest = { showCreateFolderDialog = false }, diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/MainScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/MainScreen.kt index b9e6358..dfe76d6 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/MainScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/MainScreen.kt @@ -26,28 +26,15 @@ import androidx.compose.material.icons.rounded.Devices import androidx.compose.material.icons.rounded.Folder import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Terminal -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableLongStateOf -import androidx.compose.runtime.mutableStateListOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveableStateHolder -import androidx.compose.runtime.setValue -import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -64,44 +51,16 @@ import io.github.miuzarte.scrcpyforandroid.R import io.github.miuzarte.scrcpyforandroid.constants.UiMotion import io.github.miuzarte.scrcpyforandroid.nativecore.NativeAdbService import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy -import io.github.miuzarte.scrcpyforandroid.services.AppRuntime -import io.github.miuzarte.scrcpyforandroid.services.AppScreenOn -import io.github.miuzarte.scrcpyforandroid.services.AppUpdateChecker -import io.github.miuzarte.scrcpyforandroid.services.ConnectionController -import io.github.miuzarte.scrcpyforandroid.services.ConnectionStateStore -import io.github.miuzarte.scrcpyforandroid.services.DeviceAdbAutoReconnectManager -import io.github.miuzarte.scrcpyforandroid.services.DeviceAdbConnectionCoordinator -import io.github.miuzarte.scrcpyforandroid.services.EventLogger -import io.github.miuzarte.scrcpyforandroid.services.LocalSnackbarController -import io.github.miuzarte.scrcpyforandroid.services.SnackbarController +import io.github.miuzarte.scrcpyforandroid.services.* import io.github.miuzarte.scrcpyforandroid.storage.AppSettings import io.github.miuzarte.scrcpyforandroid.storage.Settings import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings import io.github.miuzarte.scrcpyforandroid.storage.Storage.quickDevices -import io.github.miuzarte.scrcpyforandroid.ui.BlurredBar -import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur -import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableFloatingBottomBar -import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableFloatingBottomBarBlur +import io.github.miuzarte.scrcpyforandroid.ui.* import io.github.miuzarte.scrcpyforandroid.ui.component.FloatingBottomBar import io.github.miuzarte.scrcpyforandroid.ui.component.FloatingBottomBarItem -import io.github.miuzarte.scrcpyforandroid.ui.createThemeController -import io.github.miuzarte.scrcpyforandroid.ui.rememberBlurBackdrop -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import top.yukonga.miuix.kmp.basic.Icon -import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior -import top.yukonga.miuix.kmp.basic.NavigationBar -import top.yukonga.miuix.kmp.basic.NavigationBarItem -import top.yukonga.miuix.kmp.basic.Scaffold -import top.yukonga.miuix.kmp.basic.SnackbarHost -import top.yukonga.miuix.kmp.basic.SnackbarHostState -import top.yukonga.miuix.kmp.basic.Text +import kotlinx.coroutines.* +import top.yukonga.miuix.kmp.basic.* import top.yukonga.miuix.kmp.theme.MiuixTheme import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import java.io.File @@ -147,18 +106,19 @@ private enum class MainBottomTabDestination( Settings(labelResId = R.string.main_tab_settings, icon = Icons.Rounded.Settings); } -sealed interface RootScreen : NavKey { - data object Home : RootScreen - data object Advanced : RootScreen - data object About : RootScreen - data object VirtualButtonOrder : RootScreen - data object FullscreenControl : RootScreen // compatibility mode - data class ScrcpyOptionRecord(val profileId: String) : RootScreen +sealed interface RootScreen: NavKey { + data object Home: RootScreen + data object Advanced: RootScreen + data object About: RootScreen + data object VirtualButtonOrder: RootScreen + data object FullscreenControl: RootScreen // compatibility mode + data class ScrcpyOptionRecord(val profileId: String): RootScreen } @Composable fun MainScreen() { // Environment + val haptic = LocalHapticFeedback.current val context = LocalContext.current val appContext = context.applicationContext val activity = remember(context) { context as? Activity } @@ -186,7 +146,8 @@ fun MainScreen() { val tabs = remember { MainBottomTabDestination.entries } val pagerState = rememberPagerState( initialPage = MainBottomTabDestination.Devices.ordinal, - pageCount = { tabs.size }) + pageCount = { tabs.size }, + ) var selectedTabIndex by rememberSaveable { mutableIntStateOf(MainBottomTabDestination.Devices.ordinal) } @@ -204,11 +165,14 @@ fun MainScreen() { // Scroll behaviors val devicesPageScrollBehavior = MiuixScrollBehavior( - canScroll = { currentTab == MainBottomTabDestination.Devices }) + canScroll = { currentTab == MainBottomTabDestination.Devices }, + ) val terminalPageScrollBehavior = MiuixScrollBehavior( - canScroll = { currentTab == MainBottomTabDestination.Terminal }) + canScroll = { currentTab == MainBottomTabDestination.Terminal }, + ) val settingsPageScrollBehavior = MiuixScrollBehavior( - canScroll = { currentTab == MainBottomTabDestination.Settings }) + canScroll = { currentTab == MainBottomTabDestination.Settings }, + ) val advancedPageScrollBehavior = MiuixScrollBehavior( canScroll = { when (currentRootScreen) { @@ -217,7 +181,8 @@ fun MainScreen() { is RootScreen.ScrcpyOptionRecord -> true else -> false } - }) + }, + ) // Navigation helpers val rootNavigator = remember { @@ -336,13 +301,13 @@ fun MainScreen() { // Side-effect launchers and composition locals val picker = rememberLauncherForActivityResult( - ActivityResultContracts.OpenDocument() + ActivityResultContracts.OpenDocument(), ) { uri: Uri? -> if (uri == null) return@rememberLauncherForActivityResult runCatching { context.contentResolver.takePersistableUriPermission( uri, - Intent.FLAG_GRANT_READ_URI_PERMISSION + Intent.FLAG_GRANT_READ_URI_PERMISSION, ) } asBundle = asBundle.copy(customServerUri = uri.toString()) @@ -353,13 +318,13 @@ fun MainScreen() { arrayOf( "application/java-archive", "application/octet-stream", - "*/*" - ) + "*/*", + ), ) } } val terminalFontDocumentPicker = rememberLauncherForActivityResult( - ActivityResultContracts.OpenDocument() + ActivityResultContracts.OpenDocument(), ) { uri: Uri? -> if (uri == null) return@rememberLauncherForActivityResult taskScope.launch { @@ -392,7 +357,7 @@ fun MainScreen() { "application/x-font-ttf", "application/x-font-otf", "*/*", - ) + ), ) } } @@ -538,7 +503,10 @@ fun MainScreen() { tabs.forEach { tab -> NavigationBarItem( selected = currentTab == tab, - onClick = { navigateToTab(tab) }, + onClick = { + haptic.contextClick() + navigateToTab(tab) + }, icon = tab.icon, label = stringResource(tab.labelResId), ) @@ -577,7 +545,7 @@ fun MainScreen() { Modifier.layerBackdrop(glassBackdrop) } else { Modifier - } + }, ), state = pagerState, beyondViewportPageCount = 1, diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/RecordPreferencesScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/RecordPreferencesScreen.kt index 39ffd4b..03759ba 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/RecordPreferencesScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/RecordPreferencesScreen.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction @@ -47,6 +48,7 @@ import io.github.miuzarte.scrcpyforandroid.storage.Storage.scrcpyOptions import io.github.miuzarte.scrcpyforandroid.storage.Storage.scrcpyProfiles import io.github.miuzarte.scrcpyforandroid.ui.BlurredBar import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur +import io.github.miuzarte.scrcpyforandroid.ui.contextClick import io.github.miuzarte.scrcpyforandroid.ui.rememberBlurBackdrop import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -124,6 +126,8 @@ private fun RecordPreferencesPage( val taskScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.IO) } val currentSession by scrcpy.currentSessionState.collectAsState() + val haptic = LocalHapticFeedback.current + val soBundleShared by scrcpyOptions.bundleState.collectAsState() val scrcpyProfilesState by scrcpyProfiles.state.collectAsState() val sourceBundle = remember(profileId, soBundleShared, scrcpyProfilesState) { @@ -269,6 +273,7 @@ private fun RecordPreferencesPage( TextButton( text = entry.value, onClick = { + haptic.contextClick() draftTemplate = draftTemplate.replaceSelection(entry.value) }, insideMargin = PaddingValues(horizontal = 10.dp, vertical = 8.dp), diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt index 883b1bd..272ce8b 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/ScrcpyAllOptionsScreen.kt @@ -54,7 +54,7 @@ import io.github.miuzarte.scrcpyforandroid.models.ScrcpyOptions.NewDisplay import io.github.miuzarte.scrcpyforandroid.scaffolds.LazyColumn import io.github.miuzarte.scrcpyforandroid.scaffolds.OverlaySpinnerWithFallback import io.github.miuzarte.scrcpyforandroid.scaffolds.ReorderableList -import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperSlider +import io.github.miuzarte.scrcpyforandroid.scaffolds.ArrowSlider import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperTextField import io.github.miuzarte.scrcpyforandroid.scrcpy.ClientOptions import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy @@ -117,7 +117,6 @@ internal fun ScrcpyAllOptionsScreen( val blurBackdrop = rememberBlurBackdrop(LocalEnableBlur.current) val blurActive = blurBackdrop != null val scope = rememberCoroutineScope() - var showProfileMenu by rememberSaveable { mutableStateOf(false) } var showManageProfilesSheet by rememberSaveable { mutableStateOf(false) } val qdBundleShared by quickDevices.bundleState.collectAsState() val soBundleShared by scrcpyOptions.bundleState.collectAsState() @@ -235,7 +234,6 @@ internal fun ScrcpyAllOptionsScreen( text = stringResource(R.string.scrcpyopt_manage_profiles), onClick = { showManageProfilesSheet = true - showProfileMenu = false }, ) ) @@ -957,7 +955,7 @@ internal fun ScrcpyAllOptionsPage( }, enabled = soBundle.audio, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.scrcpyopt_screen_off_timeout), summary = "--screen-off-timeout", value = screenOffTimeoutPresetIndex.toFloat(), @@ -1103,7 +1101,7 @@ internal fun ScrcpyAllOptionsPage( ) }, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.scrcpyopt_audio_bitrate), summary = "--audio-bit-rate", value = ScrcpyPresets.AudioBitRate @@ -1148,7 +1146,7 @@ internal fun ScrcpyAllOptionsPage( ) }, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.scrcpyopt_video_bitrate), summary = "--video-bit-rate", value = soBundle.videoBitRate / 1_000_000f, @@ -1242,7 +1240,7 @@ internal fun ScrcpyAllOptionsPage( ) }, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.scrcpyopt_min_size_alignment), summary = "--min-size-alignment", value = minSizeAlignmentPresetIndex.toFloat(), @@ -1265,17 +1263,17 @@ internal fun ScrcpyAllOptionsPage( inputFilter = { it.filter(Char::isDigit) }, inputValueRange = 1f..16f, onInputConfirm = { raw -> - val parsed = raw.toIntOrNull() ?: return@SuperSlider + val parsed = raw.toIntOrNull() ?: return@ArrowSlider if (parsed and (parsed - 1) != 0) { AppRuntime.snackbar(R.string.scrcpyopt_min_size_alignment_invalid) - return@SuperSlider + return@ArrowSlider } soBundle = soBundle.copy( minSizeAlignment = parsed ) }, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.scrcpyopt_max_size), summary = "--max-size", value = maxSizePresetIndex.toFloat(), @@ -1306,7 +1304,7 @@ internal fun ScrcpyAllOptionsPage( ) }, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.scrcpyopt_max_fps), summary = "--max-fps", value = maxFpsPresetIndex.toFloat(), @@ -1498,7 +1496,7 @@ internal fun ScrcpyAllOptionsPage( .fillMaxWidth() .padding(all = UiSpacing.Large), ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.scrcpyopt_camera_fps), summary = "--camera-fps", value = cameraFpsPresetIndex.toFloat(), diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/SettingsScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/SettingsScreen.kt index f2b3737..0ba8e36 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/SettingsScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/SettingsScreen.kt @@ -47,7 +47,7 @@ import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing import io.github.miuzarte.scrcpyforandroid.nativecore.DirectAdbTransport import io.github.miuzarte.scrcpyforandroid.scaffolds.LazyColumn import io.github.miuzarte.scrcpyforandroid.scaffolds.SectionSmallTitle -import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperSlider +import io.github.miuzarte.scrcpyforandroid.scaffolds.ArrowSlider import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperTextField import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy import io.github.miuzarte.scrcpyforandroid.services.AppRuntime @@ -459,7 +459,7 @@ fun SettingsPage( ) }, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.pref_title_preview_card_height), summary = stringResource(R.string.pref_summary_preview_card_height), value = asBundle.devicePreviewCardHeightDp.toFloat(), @@ -598,7 +598,7 @@ fun SettingsPage( stringResource(R.string.dock_display_on) + stringResource(fullscreenVirtualButtonDock.directionLabelResId), ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.pref_title_virtual_button_height), value = asBundle.fullscreenVirtualButtonHeightDp.toFloat(), onValueChange = { @@ -637,7 +637,7 @@ fun SettingsPage( ) AnimatedVisibility(asBundle.showFullscreenFloatingButton) { Column { - SuperSlider( + ArrowSlider( title = stringResource(R.string.pref_title_floating_button_size), value = asBundle.fullscreenFloatingButtonSizeDp.toFloat(), onValueChange = { @@ -662,7 +662,7 @@ fun SettingsPage( } }, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.pref_title_floating_button_bg_opacity), value = asBundle.fullscreenFloatingButtonBackgroundAlphaPercent.toFloat(), onValueChange = { @@ -687,7 +687,7 @@ fun SettingsPage( } }, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.pref_title_floating_button_ring_opacity), value = asBundle.fullscreenFloatingButtonRingAlphaPercent.toFloat(), onValueChange = { @@ -1064,7 +1064,7 @@ fun SettingsPage( item { SectionSmallTitle(stringResource(R.string.section_terminal)) Card { - SuperSlider( + ArrowSlider( title = stringResource(R.string.pref_title_terminal_font_size), summary = stringResource(R.string.pref_summary_terminal_font_size), value = asBundle.terminalFontSizeSp, diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/VirtualButtonOrderScreen.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/VirtualButtonOrderScreen.kt index c2b866e..924421a 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/VirtualButtonOrderScreen.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/pages/VirtualButtonOrderScreen.kt @@ -6,20 +6,13 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource import io.github.miuzarte.scrcpyforandroid.R import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing @@ -29,20 +22,12 @@ import io.github.miuzarte.scrcpyforandroid.storage.Settings import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings import io.github.miuzarte.scrcpyforandroid.ui.BlurredBar import io.github.miuzarte.scrcpyforandroid.ui.LocalEnableBlur +import io.github.miuzarte.scrcpyforandroid.ui.contextClick import io.github.miuzarte.scrcpyforandroid.ui.rememberBlurBackdrop import io.github.miuzarte.scrcpyforandroid.widgets.VirtualButtonAction import io.github.miuzarte.scrcpyforandroid.widgets.VirtualButtonActions -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import top.yukonga.miuix.kmp.basic.Card -import top.yukonga.miuix.kmp.basic.Icon -import top.yukonga.miuix.kmp.basic.IconButton -import top.yukonga.miuix.kmp.basic.Scaffold -import top.yukonga.miuix.kmp.basic.ScrollBehavior -import top.yukonga.miuix.kmp.basic.TopAppBar +import kotlinx.coroutines.* +import top.yukonga.miuix.kmp.basic.* import top.yukonga.miuix.kmp.blur.layerBackdrop import top.yukonga.miuix.kmp.preference.SwitchPreference import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme @@ -51,6 +36,7 @@ import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme internal fun VirtualButtonOrderScreen( scrollBehavior: ScrollBehavior, ) { + val haptic = LocalHapticFeedback.current val navigator = LocalRootNavigator.current val blurBackdrop = rememberBlurBackdrop(LocalEnableBlur.current) val blurActive = blurBackdrop != null @@ -65,7 +51,12 @@ internal fun VirtualButtonOrderScreen( if (blurActive) Color.Transparent else colorScheme.surface, navigationIcon = { - IconButton(onClick = navigator.pop) { + IconButton( + onClick = { + haptic.contextClick() + navigator.pop() + }, + ) { Icon( imageVector = Icons.AutoMirrored.Rounded.ArrowBack, contentDescription = stringResource(R.string.cd_back), @@ -169,10 +160,10 @@ internal fun VirtualButtonOrderPage( } asBundle = asBundle.copy( virtualButtonsLayout = VirtualButtonActions - .encodeStoredLayout(buttonItems) + .encodeStoredLayout(buttonItems), ) }, - ) + ), ), ) } @@ -183,7 +174,7 @@ internal fun VirtualButtonOrderPage( .apply { add(toIndex, removeAt(fromIndex)) } asBundle = asBundle.copy( virtualButtonsLayout = VirtualButtonActions - .encodeStoredLayout(buttonItems) + .encodeStoredLayout(buttonItems), ) }, )() diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/SuperSlider.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/ArrowSlider.kt similarity index 88% rename from app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/SuperSlider.kt rename to app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/ArrowSlider.kt index 8d94577..643dc0a 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/SuperSlider.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/ArrowSlider.kt @@ -13,13 +13,16 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import androidx.compose.ui.res.stringResource import io.github.miuzarte.scrcpyforandroid.R +import io.github.miuzarte.scrcpyforandroid.ui.contextClick import top.yukonga.miuix.kmp.basic.ButtonDefaults import top.yukonga.miuix.kmp.basic.Slider +import top.yukonga.miuix.kmp.basic.SliderDefaults.SliderHapticEffect import top.yukonga.miuix.kmp.basic.Text import top.yukonga.miuix.kmp.basic.TextButton import top.yukonga.miuix.kmp.overlay.OverlayDialog @@ -28,19 +31,22 @@ import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme import top.yukonga.miuix.kmp.theme.MiuixTheme.textStyles @Composable -fun SuperSlider( +fun ArrowSlider( title: String, summary: String? = null, value: Float, onValueChange: (Float) -> Unit, valueRange: ClosedFloatingPointRange, steps: Int, + onValueChangeFinished: (() -> Unit)? = null, enabled: Boolean = true, + hapticEffect: SliderHapticEffect = SliderHapticEffect.Edge, + showKeyPoints: Boolean = false, + keyPoints: List = emptyList(), + magnetThreshold: Float = 0.02f, unit: String = "", zeroStateText: String? = null, showUnitWhenZeroState: Boolean = false, - showKeyPoints: Boolean = false, - keyPoints: List = emptyList(), displayFormatter: (Float) -> String = { it.toInt().toString() }, displayText: String? = null, inputTitle: String = title, @@ -52,6 +58,8 @@ fun SuperSlider( inputValueRange: ClosedFloatingPointRange? = null, onInputConfirm: (String) -> Unit, ) { + val haptic = LocalHapticFeedback.current + var showInputDialog by remember { mutableStateOf(false) } var holdArrow by remember { mutableStateOf(false) } @@ -59,6 +67,7 @@ fun SuperSlider( title = title, summary = summary, onClick = { + haptic.contextClick() showInputDialog = true holdArrow = true }, @@ -83,9 +92,12 @@ fun SuperSlider( onValueChange = onValueChange, valueRange = valueRange, steps = steps, + onValueChangeFinished = onValueChangeFinished, + enabled = enabled, + hapticEffect = hapticEffect, showKeyPoints = showKeyPoints, keyPoints = keyPoints, - enabled = enabled, + magnetThreshold = magnetThreshold, ) }, ) @@ -122,6 +134,8 @@ private fun SliderInputDialog( onDismissFinished: () -> Unit, onConfirm: (String) -> Unit, ) { + val haptic = LocalHapticFeedback.current + OverlayDialog( show = showDialog, title = title, @@ -150,13 +164,17 @@ private fun SliderInputDialog( Row(horizontalArrangement = Arrangement.SpaceBetween) { TextButton( text = stringResource(R.string.button_cancel), - onClick = onDismissRequest, + onClick = { + haptic.contextClick() + onDismissRequest() + }, modifier = Modifier.weight(1f), ) Spacer(Modifier.width(20.dp)) TextButton( text = stringResource(R.string.button_confirm), onClick = { + haptic.contextClick() val inputValue = text.toFloatOrNull() ?: 0f if (inputValue >= inputValueRange.start && inputValue <= inputValueRange.endInclusive) { onConfirm(text.trim()) diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/SuperTextField.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/SuperTextField.kt index c80e5fb..d3f000f 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/SuperTextField.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/scaffolds/SuperTextField.kt @@ -69,9 +69,9 @@ fun SuperTextField( onFocusGained: (() -> Unit)? = null, onFocusLost: (() -> Unit)? = null, insideMargin: DpSize = DpSize(16.dp, 16.dp), - backgroundColor: Color = colorScheme.secondaryContainer, - labelColor: Color = colorScheme.onSecondaryContainer, - borderColor: Color = colorScheme.primary, + // backgroundColor: Color = colorScheme.secondaryContainer, + // labelColor: Color = colorScheme.onSecondaryContainer, + // borderColor: Color = colorScheme.primary, textStyle: TextStyle = textStyles.main, cursorBrush: Brush = SolidColor(colorScheme.primary), leadingIcon: @Composable (() -> Unit)? = null, @@ -106,9 +106,9 @@ fun SuperTextField( keyboardActions = keyboardActions, visualTransformation = visualTransformation, insideMargin = insideMargin, - backgroundColor = backgroundColor, - labelColor = labelColor, - borderColor = borderColor, + // backgroundColor = backgroundColor, + // labelColor = labelColor, + // borderColor = borderColor, textStyle = textStyle, cursorBrush = cursorBrush, leadingIcon = leadingIcon, diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/ui/ThemePreferences.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/ui/ThemePreferences.kt index 23409de..918bd76 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/ui/ThemePreferences.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/ui/ThemePreferences.kt @@ -9,9 +9,9 @@ import androidx.compose.ui.platform.LocalDensity import top.yukonga.miuix.kmp.blur.BlendColorEntry import top.yukonga.miuix.kmp.blur.BlurColors import top.yukonga.miuix.kmp.blur.LayerBackdrop -import top.yukonga.miuix.kmp.blur.isRenderEffectSupported import top.yukonga.miuix.kmp.blur.rememberLayerBackdrop import top.yukonga.miuix.kmp.blur.textureBlur +import top.yukonga.miuix.kmp.shader.isRenderEffectSupported import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme val LocalEnableBlur = staticCompositionLocalOf { false } diff --git a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/widgets/DeviceWidgets.kt b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/widgets/DeviceWidgets.kt index e997f5d..cc4cdc4 100644 --- a/app/src/main/java/io/github/miuzarte/scrcpyforandroid/widgets/DeviceWidgets.kt +++ b/app/src/main/java/io/github/miuzarte/scrcpyforandroid/widgets/DeviceWidgets.kt @@ -57,7 +57,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource @@ -79,7 +78,7 @@ import io.github.miuzarte.scrcpyforandroid.constants.Defaults import io.github.miuzarte.scrcpyforandroid.constants.ScrcpyPresets import io.github.miuzarte.scrcpyforandroid.constants.UiSpacing import io.github.miuzarte.scrcpyforandroid.models.DeviceShortcut -import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperSlider +import io.github.miuzarte.scrcpyforandroid.scaffolds.ArrowSlider import io.github.miuzarte.scrcpyforandroid.scaffolds.SuperTextField import io.github.miuzarte.scrcpyforandroid.scrcpy.Scrcpy import io.github.miuzarte.scrcpyforandroid.scrcpy.Shared.Codec @@ -587,7 +586,7 @@ internal fun ConfigPanel( enabled = !sessionStarted && soBundle.audio, ) AnimatedVisibility(audioBitRateVisibility) { - SuperSlider( + ArrowSlider( title = stringResource(R.string.device_config_audio_bitrate), summary = "--audio-bit-rate", value = ScrcpyPresets.AudioBitRate @@ -634,7 +633,7 @@ internal fun ConfigPanel( }, enabled = !sessionStarted, ) - SuperSlider( + ArrowSlider( title = stringResource(R.string.device_config_video_bitrate), summary = "--video-bit-rate", value = soBundle.videoBitRate / 1_000_000f, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b7dcb06..1bf356e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "9.2.0" +agp = "9.2.1" bcpkixJdk18on = "1.83" boringssl = "20251124" conscryptAndroid = "2.5.3" diff --git a/submodule/miuix b/submodule/miuix index 127d10c..9b86e77 160000 --- a/submodule/miuix +++ b/submodule/miuix @@ -1 +1 @@ -Subproject commit 127d10c6cb66da13889f55ec85b5abb60a27e38e +Subproject commit 9b86e772d7b2a36701e07d784f6cbdb7c27062b3