From 4b903cf5e0b86608a29b8f35140e0b592364b386 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 21 Jul 2026 17:56:00 +0800 Subject: [PATCH] refactor(main): unify UI interactions with MainAction dispatcher Refactored MainActivity and its Compose UI components to use a single onAction: (MainAction) -> Unit callback. This significantly simplifies component signatures and centralizes interaction logic. - Updated MainContract.kt to include all UI-triggered actions. - Modified MainScreen and sub-components (TopBar, BottomBar, Drawer, etc.) to use the unified onAction dispatcher. - Implemented a centralized action handler in MainActivity to dispatch between ViewModel and Activity-specific tasks. - Removed redundant ImportConfigViaSub action. - Moved QR code generation state into MainUiState for MVI consistency. This change reduces parameter drilling and improves maintainability of the main interface. --- .../com/v2ray/ang/ui/main/MainActivity.kt | 52 +++++-------- .../com/v2ray/ang/ui/main/MainBottomBar.kt | 7 +- .../com/v2ray/ang/ui/main/MainContract.kt | 19 ++++- .../com/v2ray/ang/ui/main/MainDataSource.kt | 2 + .../com/v2ray/ang/ui/main/MainImportMenu.kt | 69 +++++++---------- .../com/v2ray/ang/ui/main/MainRepository.kt | 6 ++ .../java/com/v2ray/ang/ui/main/MainScreen.kt | 75 +++++-------------- .../java/com/v2ray/ang/ui/main/MainTopBar.kt | 33 +++----- .../com/v2ray/ang/viewmodel/MainViewModel.kt | 21 +++++- 9 files changed, 123 insertions(+), 161 deletions(-) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainActivity.kt index 314a604f..056a396f 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainActivity.kt @@ -2,7 +2,6 @@ package com.v2ray.ang.ui.main import android.app.Activity import android.content.Intent -import android.graphics.Bitmap import android.net.VpnService import android.os.Build import android.os.Bundle @@ -103,40 +102,33 @@ class MainActivity : HelperBaseComponentActivity() { override fun ScreenContent() { MainScreen( mainViewModel = mainViewModel, - onFabClick = ::handleFabAction, - onTestClick = ::handleLayoutTestClick, - onNavigate = ::navigateTo, - onImportManually = ::importManually, - onImportQRcode = ::importQRcode, - onImportClipboard = ::importClipboard, - onImportLocal = ::importConfigLocal, - onSubUpdate = ::importConfigViaSub, - onExportAll = { mainViewModel.onAction(MainAction.ExportAll) }, - onRealPingAll = { mainViewModel.onAction(MainAction.TestAllServers) }, - onRestartService = ::restartV2Ray, - onDelAllConfig = { mainViewModel.onAction(MainAction.RemoveAllServers) }, - onDelDuplicateConfig = { mainViewModel.onAction(MainAction.RemoveDuplicateServers) }, - onDelInvalidConfig = { mainViewModel.onAction(MainAction.RemoveInvalidServers) }, - onSortByTestResults = { mainViewModel.onAction(MainAction.SortByTestResults) }, - onEditServer = ::editServer, - onRemoveServer = { guid -> mainViewModel.onAction(MainAction.RemoveServer(guid)) }, - onSelectServer = ::setSelectServer, - onShareQRCode = ::getShareQRCodeBitmap, - onShareClipboard = ::shareToClipboard, - onShareFullContent = ::shareFullContentAsync, - onSubscriptionIdChanged = { id -> mainViewModel.onAction(MainAction.SelectGroup(id)) }, - onLocateSelectedServer = { mainViewModel.triggerLocateSelectedServer() }, + onAction = { action -> + when (action) { + MainAction.ToggleService -> handleFabAction() + MainAction.TestCurrentServer -> handleLayoutTestClick() + MainAction.ImportQRcode -> importQRcode() + MainAction.ImportClipboard -> importClipboard() + MainAction.ImportConfigLocal -> importConfigLocal() + is MainAction.ImportManually -> importManually(action.type) + MainAction.RestartService -> restartV2Ray() + MainAction.LocateSelectedServer -> mainViewModel.triggerLocateSelectedServer() + is MainAction.SelectServer -> setSelectServer(action.guid) + is MainAction.EditServer -> editServer(action.guid, action.profile) + is MainAction.ShareClipboard -> shareToClipboard(action.guid) + is MainAction.ShareFullContent -> shareFullContentAsync(action.guid) + else -> mainViewModel.onAction(action) + } + }, + onNavigate = { route -> navigateTo(route) }, shareMethodEntries = resources.getStringArray(R.array.share_method).toList(), shareMethodMoreEntries = resources.getStringArray(R.array.share_method_more).toList() ) } - fun getShareQRCodeBitmap(guid: String): Bitmap? = AngConfigManager.share2QRCode(guid) - - fun shareToClipboard(guid: String): Boolean = + private fun shareToClipboard(guid: String): Boolean = AngConfigManager.share2Clipboard(this, guid) == 0 - fun shareFullContentAsync(guid: String) { + private fun shareFullContentAsync(guid: String) { lifecycleScope.launch(Dispatchers.IO) { val result = AngConfigManager.shareFullContent2Clipboard(this@MainActivity, guid) withContext(Dispatchers.Main) { @@ -258,10 +250,6 @@ class MainActivity : HelperBaseComponentActivity() { } } - private fun importConfigViaSub() { - mainViewModel.onAction(MainAction.ImportConfigViaSub) - } - private fun editServer(guid: String, profile: ProfileItem) { val activityClass = when (profile.configType) { EConfigType.CUSTOM -> ServerCustomConfigActivity::class.java diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainBottomBar.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainBottomBar.kt index 9d42426b..feef5795 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainBottomBar.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainBottomBar.kt @@ -36,8 +36,7 @@ fun MainBottomBar( displayText: String, isRunning: Boolean, isDarkTheme: Boolean, - onTestClick: () -> Unit, - onFabClick: () -> Unit + onAction: (MainAction) -> Unit ) { Box(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) { @@ -47,7 +46,7 @@ fun MainBottomBar( .fillMaxWidth() .windowInsetsPadding(WindowInsets.navigationBars) .height(64.dp) - .clickable(onClick = onTestClick), + .clickable(onClick = { onAction(MainAction.TestCurrentServer) }), color = MaterialTheme.colorScheme.surface, tonalElevation = 0.dp ) { @@ -63,7 +62,7 @@ fun MainBottomBar( } } FloatingActionButton( - onClick = onFabClick, + onClick = { onAction(MainAction.ToggleService) }, modifier = Modifier .align(Alignment.TopEnd) .padding(end = 24.dp) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainContract.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainContract.kt index 4d3978a2..ef59674c 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainContract.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainContract.kt @@ -15,7 +15,8 @@ data class MainUiState( val statusText: String = "", val locateTarget: LocateTarget? = null, val confirmRemove: Boolean = false, - val doubleColumnDisplay: Boolean = false + val doubleColumnDisplay: Boolean = false, + val shareQRCodeBitmap: android.graphics.Bitmap? = null ) /** @@ -34,15 +35,27 @@ sealed interface MainAction { data object SortByTestResults : MainAction data object UpdateSubscriptions : MainAction data object ExportAll : MainAction - data object ImportConfigViaSub : MainAction + + data object ImportQRcode : MainAction + data object ImportClipboard : MainAction + data object ImportConfigLocal : MainAction + data class ImportManually(val type: Int) : MainAction + data object RestartService : MainAction + data object LocateSelectedServer : MainAction data class SelectGroup(val groupId: String) : MainAction data class SelectServer(val guid: String) : MainAction data class RemoveServer(val guid: String) : MainAction + data class EditServer(val guid: String, val profile: com.v2ray.ang.dto.entities.ProfileItem) : MainAction data class Search(val query: String) : MainAction data class SwapServer(val fromIndex: Int, val toIndex: Int) : MainAction + data class ShareQRCode(val guid: String) : MainAction + data class ShareClipboard(val guid: String) : MainAction + data class ShareFullContent(val guid: String) : MainAction + data object DismissQRCodeDialog : MainAction + data class ImportBatchConfig(val configText: String) : MainAction - data class LocateHandled(val target: LocateTarget) : MainAction + data class LocateHandled(val target: com.v2ray.ang.dto.LocateTarget) : MainAction } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainDataSource.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainDataSource.kt index 842b097b..8a9fb5c3 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainDataSource.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainDataSource.kt @@ -55,6 +55,8 @@ interface MainDataSource : Closeable { fun updateConfigViaSub(subscriptionCache: SubscriptionCache): SubscriptionUpdateResult fun shareNonCustomConfigsToClipboard(guids: List): Int + fun share2QRCode(guid: String): android.graphics.Bitmap? + fun share2Clipboard(guid: String): Boolean fun sendMsg2Service(msgId: Int, content: String) fun sendMsg2TestService(msg: TestServiceMessage) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainImportMenu.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainImportMenu.kt index fe6f5912..0770a1ca 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainImportMenu.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainImportMenu.kt @@ -14,80 +14,72 @@ import com.v2ray.ang.extension.isComplexType @Composable fun ImportMenuContent( - onImportQRcode: () -> Unit, - onImportClipboard: () -> Unit, - onImportLocal: () -> Unit, - onImportManually: (Int) -> Unit + onAction: (MainAction) -> Unit ) { DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_qrcode)) }, - onClick = onImportQRcode + onClick = { onAction(MainAction.ImportQRcode) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_clipboard)) }, - onClick = onImportClipboard + onClick = { onAction(MainAction.ImportClipboard) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_local)) }, - onClick = onImportLocal + onClick = { onAction(MainAction.ImportConfigLocal) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_policy_group)) }, - onClick = { onImportManually(EConfigType.POLICYGROUP.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.POLICYGROUP.value)) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_proxy_chain)) }, - onClick = { onImportManually(EConfigType.PROXYCHAIN.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.PROXYCHAIN.value)) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_manually_vmess)) }, - onClick = { onImportManually(EConfigType.VMESS.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.VMESS.value)) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_manually_vless)) }, - onClick = { onImportManually(EConfigType.VLESS.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.VLESS.value)) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_manually_ss)) }, - onClick = { onImportManually(EConfigType.SHADOWSOCKS.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.SHADOWSOCKS.value)) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_manually_socks)) }, - onClick = { onImportManually(EConfigType.SOCKS.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.SOCKS.value)) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_manually_http)) }, - onClick = { onImportManually(EConfigType.HTTP.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.HTTP.value)) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_manually_trojan)) }, - onClick = { onImportManually(EConfigType.TROJAN.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.TROJAN.value)) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_manually_wireguard)) }, - onClick = { onImportManually(EConfigType.WIREGUARD.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.WIREGUARD.value)) } ) DropdownMenuItem( text = { Text(stringResource(R.string.menu_item_import_config_manually_hysteria2)) }, - onClick = { onImportManually(EConfigType.HYSTERIA2.value) } + onClick = { onAction(MainAction.ImportManually(EConfigType.HYSTERIA2.value)) } ) } @Composable fun MoreMenuContent( - onRestartService: () -> Unit, + onAction: (MainAction) -> Unit, onDelAllConfig: () -> Unit, onDelDuplicateConfig: () -> Unit, - onDelInvalidConfig: () -> Unit, - onExportAll: () -> Unit, - onRealPingAll: () -> Unit, - onLocateSelectedServer: () -> Unit, - onSortByTestResults: () -> Unit, - onSubUpdate: () -> Unit + onDelInvalidConfig: () -> Unit ) { DropdownMenuItem( text = { Text(stringResource(R.string.title_service_restart)) }, - onClick = onRestartService + onClick = { onAction(MainAction.RestartService) } ) DropdownMenuItem( text = { Text(stringResource(R.string.title_del_all_config)) }, @@ -103,23 +95,23 @@ fun MoreMenuContent( ) DropdownMenuItem( text = { Text(stringResource(R.string.title_export_all)) }, - onClick = onExportAll + onClick = { onAction(MainAction.ExportAll) } ) DropdownMenuItem( text = { Text(stringResource(R.string.title_real_ping_all_server)) }, - onClick = onRealPingAll + onClick = { onAction(MainAction.TestAllServers) } ) DropdownMenuItem( text = { Text(stringResource(R.string.title_locate_selected_config)) }, - onClick = onLocateSelectedServer + onClick = { onAction(MainAction.LocateSelectedServer) } ) DropdownMenuItem( text = { Text(stringResource(R.string.title_sort_by_test_results)) }, - onClick = onSortByTestResults + onClick = { onAction(MainAction.SortByTestResults) } ) DropdownMenuItem( text = { Text(stringResource(R.string.title_sub_update)) }, - onClick = onSubUpdate + onClick = { onAction(MainAction.UpdateSubscriptions) } ) } @@ -131,12 +123,7 @@ fun ShareMethodDialog( shareMethodEntries: List, shareMethodMoreEntries: List, onDismiss: () -> Unit, - onShareQRCode: (String) -> Bitmap?, - onShareClipboard: (String) -> Boolean, - onShareFullContent: (String) -> Unit, - onEditServer: (String, ProfileItem) -> Unit, - onRemoveServer: (String) -> Unit, - showQRCodeBitmap: (Bitmap?) -> Unit + onAction: (MainAction) -> Unit ) { val isCustom = profile.configType.isComplexType() val (shareOptions, skip) = if (more) { @@ -151,11 +138,11 @@ fun ShareMethodDialog( onSelected = { index, _ -> onDismiss() when (index + skip) { - 0 -> showQRCodeBitmap(onShareQRCode(guid)) - 1 -> onShareClipboard(guid) - 2 -> onShareFullContent(guid) - 3 -> onEditServer(guid, profile) - 4 -> onRemoveServer(guid) + 0 -> onAction(MainAction.ShareQRCode(guid)) + 1 -> onAction(MainAction.ShareClipboard(guid)) + 2 -> onAction(MainAction.ShareFullContent(guid)) + 3 -> onAction(MainAction.EditServer(guid, profile)) + 4 -> onAction(MainAction.RemoveServer(guid)) } }, onDismiss = onDismiss diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainRepository.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainRepository.kt index 0ff19beb..38672692 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainRepository.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainRepository.kt @@ -201,6 +201,12 @@ class MainRepository( override fun shareNonCustomConfigsToClipboard(guids: List): Int = AngConfigManager.shareNonCustomConfigsToClipboard(app, guids) + override fun share2QRCode(guid: String): android.graphics.Bitmap? = + AngConfigManager.share2QRCode(guid) + + override fun share2Clipboard(guid: String): Boolean = + AngConfigManager.share2Clipboard(app, guid) == 0 + override fun sendMsg2Service(msgId: Int, content: String) = MessageUtil.sendMsg2Service(app, msgId, content) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainScreen.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainScreen.kt index 1d791169..1f6d2228 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainScreen.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainScreen.kt @@ -1,6 +1,5 @@ package com.v2ray.ang.ui.main -import android.graphics.Bitmap import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize @@ -41,29 +40,8 @@ import kotlinx.coroutines.launch @Composable fun MainScreen( mainViewModel: MainViewModel, - onFabClick: () -> Unit, - onTestClick: () -> Unit, + onAction: (MainAction) -> Unit, onNavigate: (String) -> Unit, - onImportManually: (Int) -> Unit, - onImportQRcode: () -> Unit, - onImportClipboard: () -> Unit, - onImportLocal: () -> Unit, - onSubUpdate: () -> Unit, - onExportAll: () -> Unit, - onRealPingAll: () -> Unit, - onRestartService: () -> Unit, - onDelAllConfig: () -> Unit, - onDelDuplicateConfig: () -> Unit, - onDelInvalidConfig: () -> Unit, - onSortByTestResults: () -> Unit, - onEditServer: (String, ProfileItem) -> Unit, - onRemoveServer: (String) -> Unit, - onSelectServer: (String) -> Unit, - onShareQRCode: (String) -> Bitmap?, - onShareClipboard: (String) -> Boolean, - onShareFullContent: (String) -> Unit, - onSubscriptionIdChanged: (String) -> Unit, - onLocateSelectedServer: () -> Unit, shareMethodEntries: List, shareMethodMoreEntries: List ) { @@ -75,6 +53,7 @@ fun MainScreen( val selectedGuid = uiState.selectedGuid val doubleColumnDisplay = uiState.doubleColumnDisplay val confirmRemove = uiState.confirmRemove + val shareQRCodeBitmap = uiState.shareQRCodeBitmap val isDarkTheme = LocalDarkTheme.current val drawerState = rememberDrawerState(DrawerValue.Closed) @@ -87,7 +66,6 @@ fun MainScreen( var showRemoveConfirm by remember { mutableStateOf(null) } var shareTarget by remember { mutableStateOf?>(null) } - var showQRCodeBitmap by remember { mutableStateOf(null) } val pagerState = rememberPagerState( initialPage = 0, @@ -125,7 +103,7 @@ fun MainScreen( .collect { page -> val currentGroups = latestGroups if (!latestLocateInProgress && page in currentGroups.indices) { - onSubscriptionIdChanged(currentGroups[page].id) + onAction(MainAction.SelectGroup(currentGroups[page].id)) } } } @@ -145,7 +123,7 @@ fun MainScreen( animateAdjacentPage = false ) } - onSubscriptionIdChanged(target.groupId) + onAction(MainAction.SelectGroup(target.groupId)) repeat(10) { val ready = if (latestDoubleColumnDisplay) { @@ -182,16 +160,16 @@ fun MainScreen( MainDialogs( showDelAllConfirm = showDelAllConfirm, onDismissDelAll = { showDelAllConfirm = false }, - onConfirmDelAll = { showDelAllConfirm = false; onDelAllConfig() }, + onConfirmDelAll = { showDelAllConfirm = false; onAction(MainAction.RemoveAllServers) }, showDelDuplicateConfirm = showDelDuplicateConfirm, onDismissDelDuplicate = { showDelDuplicateConfirm = false }, - onConfirmDelDuplicate = { showDelDuplicateConfirm = false; onDelDuplicateConfig() }, + onConfirmDelDuplicate = { showDelDuplicateConfirm = false; onAction(MainAction.RemoveDuplicateServers) }, showDelInvalidConfirm = showDelInvalidConfirm, onDismissDelInvalid = { showDelInvalidConfirm = false }, - onConfirmDelInvalid = { showDelInvalidConfirm = false; onDelInvalidConfig() }, + onConfirmDelInvalid = { showDelInvalidConfirm = false; onAction(MainAction.RemoveInvalidServers) }, showRemoveConfirm = showRemoveConfirm, onDismissRemove = { showRemoveConfirm = null }, - onConfirmRemove = { guid -> showRemoveConfirm = null; onRemoveServer(guid) } + onConfirmRemove = { guid -> showRemoveConfirm = null; onAction(MainAction.RemoveServer(guid)) } ) if (shareTarget != null) { @@ -203,16 +181,11 @@ fun MainScreen( shareMethodEntries = shareMethodEntries, shareMethodMoreEntries = shareMethodMoreEntries, onDismiss = { shareTarget = null }, - onShareQRCode = onShareQRCode, - onShareClipboard = onShareClipboard, - onShareFullContent = onShareFullContent, - onEditServer = onEditServer, - onRemoveServer = onRemoveServer, - showQRCodeBitmap = { bitmap -> showQRCodeBitmap = bitmap } + onAction = onAction ) } - if (showQRCodeBitmap != null) { - QRCodeDialog(bitmap = showQRCodeBitmap, onDismiss = { showQRCodeBitmap = null }) + if (shareQRCodeBitmap != null) { + QRCodeDialog(bitmap = shareQRCodeBitmap, onDismiss = { onAction(MainAction.DismissQRCodeDialog) }) } ModalNavigationDrawer( @@ -235,28 +208,19 @@ fun MainScreen( searchQuery = searchQuery, onSearchQueryChange = { query: String -> searchQuery = query - mainViewModel.filterConfig(query) + onAction(MainAction.Search(query)) }, onSearchClose = { searchQuery = "" - mainViewModel.filterConfig("") + onAction(MainAction.Search("")) showSearch = false }, onSearchToggle = { show: Boolean -> showSearch = show }, onMenuClick = { scope.launch { drawerState.open() } }, - onImportQRcode = onImportQRcode, - onImportClipboard = onImportClipboard, - onImportLocal = onImportLocal, - onImportManually = onImportManually, - onRestartService = onRestartService, + onAction = onAction, onDelAllConfig = { showDelAllConfirm = true }, onDelDuplicateConfig = { showDelDuplicateConfirm = true }, - onDelInvalidConfig = { showDelInvalidConfirm = true }, - onExportAll = onExportAll, - onRealPingAll = onRealPingAll, - onLocateSelectedServer = onLocateSelectedServer, - onSortByTestResults = onSortByTestResults, - onSubUpdate = onSubUpdate + onDelInvalidConfig = { showDelInvalidConfirm = true } ) }, bottomBar = { @@ -264,8 +228,7 @@ fun MainScreen( displayText = displayText, isRunning = isRunning, isDarkTheme = isDarkTheme, - onTestClick = onTestClick, - onFabClick = onFabClick + onAction = onAction ) }, floatingActionButton = {}, @@ -312,8 +275,8 @@ fun MainScreen( searchQuery = searchQuery, lazyListStates = lazyListStates, lazyGridStates = lazyGridStates, - onSelectServer = onSelectServer, - onEditServer = onEditServer, + onSelectServer = { guid -> onAction(MainAction.SelectServer(guid)) }, + onEditServer = { guid, profile -> onAction(MainAction.EditServer(guid, profile)) }, onShareServer = { guid, profile -> shareTarget = Triple(guid, profile, false) }, @@ -322,7 +285,7 @@ fun MainScreen( }, onRemoveServer = { guid -> if (confirmRemove) showRemoveConfirm = guid - else onRemoveServer(guid) + else onAction(MainAction.RemoveServer(guid)) }, contentPadding = PaddingValues( start = 0.dp, diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainTopBar.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainTopBar.kt index 3f9cfcc2..ebf56ac8 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainTopBar.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainTopBar.kt @@ -39,19 +39,10 @@ fun MainTopBar( onSearchClose: () -> Unit, onSearchToggle: (Boolean) -> Unit, onMenuClick: () -> Unit, - onImportQRcode: () -> Unit, - onImportClipboard: () -> Unit, - onImportLocal: () -> Unit, - onImportManually: (Int) -> Unit, - onRestartService: () -> Unit, + onAction: (MainAction) -> Unit, onDelAllConfig: () -> Unit, onDelDuplicateConfig: () -> Unit, - onDelInvalidConfig: () -> Unit, - onExportAll: () -> Unit, - onRealPingAll: () -> Unit, - onLocateSelectedServer: () -> Unit, - onSortByTestResults: () -> Unit, - onSubUpdate: () -> Unit + onDelInvalidConfig: () -> Unit ) { var showImportMenu by remember { mutableStateOf(false) } var showMenu by remember { mutableStateOf(false) } @@ -101,10 +92,10 @@ fun MainTopBar( .verticalScrollbar(importMenuScrollState) ) { ImportMenuContent( - onImportQRcode = { showImportMenu = false; onImportQRcode() }, - onImportClipboard = { showImportMenu = false; onImportClipboard() }, - onImportLocal = { showImportMenu = false; onImportLocal() }, - onImportManually = { type -> showImportMenu = false; onImportManually(type) } + onAction = { action -> + showImportMenu = false + onAction(action) + } ) } } @@ -122,15 +113,13 @@ fun MainTopBar( .verticalScrollbar(moreMenuScrollState) ) { MoreMenuContent( - onRestartService = { showMenu = false; onRestartService() }, + onAction = { action -> + showMenu = false + onAction(action) + }, onDelAllConfig = { showMenu = false; onDelAllConfig() }, onDelDuplicateConfig = { showMenu = false; onDelDuplicateConfig() }, - onDelInvalidConfig = { showMenu = false; onDelInvalidConfig() }, - onExportAll = { showMenu = false; onExportAll() }, - onRealPingAll = { showMenu = false; onRealPingAll() }, - onLocateSelectedServer = { showMenu = false; onLocateSelectedServer() }, - onSortByTestResults = { showMenu = false; onSortByTestResults() }, - onSubUpdate = { showMenu = false; onSubUpdate() } + onDelInvalidConfig = { showMenu = false; onDelInvalidConfig() } ) } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt index b8a50375..f9baa2b8 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -173,7 +173,6 @@ class MainViewModel( MainAction.SortByTestResults -> sortByTestResultsAsync() MainAction.UpdateSubscriptions -> importConfigViaSub() MainAction.ExportAll -> exportAllAsync() - MainAction.ImportConfigViaSub -> importConfigViaSub() is MainAction.SelectGroup -> subscriptionIdChanged(action.groupId) is MainAction.SelectServer -> updateSelectedGuid(action.guid) is MainAction.RemoveServer -> removeServerAndRefresh(action.guid) @@ -181,9 +180,25 @@ class MainViewModel( is MainAction.SwapServer -> swapServer(action.fromIndex, action.toIndex) is MainAction.ImportBatchConfig -> importBatchConfig(action.configText) is MainAction.LocateHandled -> consumeLocateTarget(action.target) + is MainAction.ShareQRCode -> { + val bitmap = dataSource.share2QRCode(action.guid) + _uiState.update { it.copy(shareQRCodeBitmap = bitmap) } + } + MainAction.DismissQRCodeDialog -> { + _uiState.update { it.copy(shareQRCodeBitmap = null) } + } MainAction.ToggleService, - MainAction.TestCurrentServer -> { - // Platform-specific, handled by Activity + MainAction.TestCurrentServer, + MainAction.ImportQRcode, + MainAction.ImportClipboard, + MainAction.ImportConfigLocal, + is MainAction.ImportManually, + MainAction.RestartService, + MainAction.LocateSelectedServer, + is MainAction.EditServer, + is MainAction.ShareClipboard, + is MainAction.ShareFullContent -> { + // Handled by Activity via its onAction lambda } } }