diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5678dd9..cc7e170 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -211,6 +211,7 @@ dependencies { // API 23+ dependencies (play/other) "playImplementation"("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion23") "playImplementation"("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion23") + "playImplementation"("androidx.lifecycle:lifecycle-process:$lifecycleVersion23") "playImplementation"("androidx.room:room-runtime:$roomVersion23") "playImplementation"("androidx.work:work-runtime-ktx:$workVersion23") "playImplementation"("androidx.camera:camera-view:$cameraVersion23") @@ -222,6 +223,7 @@ dependencies { "otherImplementation"("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion23") "otherImplementation"("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion23") + "otherImplementation"("androidx.lifecycle:lifecycle-process:$lifecycleVersion23") "otherImplementation"("androidx.room:room-runtime:$roomVersion23") "otherImplementation"("androidx.work:work-runtime-ktx:$workVersion23") "otherImplementation"("androidx.camera:camera-view:$cameraVersion23") @@ -233,6 +235,7 @@ dependencies { // API 21 dependencies (otherLegacy) "otherLegacyImplementation"("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion21") "otherLegacyImplementation"("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion21") + "otherLegacyImplementation"("androidx.lifecycle:lifecycle-process:$lifecycleVersion21") "otherLegacyImplementation"("androidx.room:room-runtime:$roomVersion21") "otherLegacyImplementation"("androidx.work:work-runtime-ktx:$workVersion21") "otherLegacyImplementation"("androidx.camera:camera-view:$cameraVersion21") diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt b/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt index de9f4c6..eb05c2e 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt @@ -61,6 +61,7 @@ import androidx.compose.material3.TextButton import androidx.compose.material3.rememberModalBottomSheetState 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 @@ -541,14 +542,7 @@ class MainActivity : ComponentActivity(), ServiceConnection.Callback { val connectionsViewModel: ConnectionsViewModel? = if (isConnectionsRoute) { - viewModel( - factory = object : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - @Suppress("UNCHECKED_CAST") - return ConnectionsViewModel(dashboardViewModel.commandClient) as T - } - } - ) + viewModel() } else { null } @@ -973,18 +967,21 @@ class MainActivity : ComponentActivity(), ServiceConnection.Callback { // Connections ModalBottomSheet if (showConnectionsSheet && !useNavigationRail) { val connectionsSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - val connectionsViewModel: ConnectionsViewModel = viewModel( - factory = object : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - @Suppress("UNCHECKED_CAST") - return ConnectionsViewModel(dashboardViewModel.commandClient) as T - } - } - ) + val connectionsViewModel: ConnectionsViewModel = viewModel() val connectionsUiState by connectionsViewModel.uiState.collectAsState() var selectedConnectionId by remember { mutableStateOf(null) } val selectedConnection = connectionsUiState.allConnections.find { it.id == selectedConnectionId } + LaunchedEffect(Unit) { + connectionsViewModel.setVisible(true) + } + + DisposableEffect(Unit) { + onDispose { + connectionsViewModel.setVisible(false) + } + } + ModalBottomSheet( onDismissRequest = { showConnectionsSheet = false diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsScreen.kt index 6593864..b85aaef 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsScreen.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsScreen.kt @@ -43,6 +43,7 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar 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 @@ -334,6 +335,16 @@ fun ConnectionsScreen( ) { val uiState by viewModel.uiState.collectAsState() + LaunchedEffect(Unit) { + viewModel.setVisible(true) + } + + DisposableEffect(Unit) { + onDispose { + viewModel.setVisible(false) + } + } + LaunchedEffect(serviceStatus) { viewModel.updateServiceStatus(serviceStatus) } diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsViewModel.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsViewModel.kt index 93117bc..9cd0c78 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsViewModel.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/connections/ConnectionsViewModel.kt @@ -1,6 +1,7 @@ package io.nekohasekai.sfa.compose.screen.connections import androidx.lifecycle.viewModelScope +import io.nekohasekai.libbox.ConnectionEvents import io.nekohasekai.libbox.Connections import io.nekohasekai.libbox.Libbox import io.nekohasekai.sfa.compose.base.BaseViewModel @@ -10,14 +11,16 @@ import io.nekohasekai.sfa.ktx.toList import io.nekohasekai.sfa.compose.model.Connection import io.nekohasekai.sfa.compose.model.ConnectionSort import io.nekohasekai.sfa.compose.model.ConnectionStateFilter +import io.nekohasekai.sfa.utils.AppLifecycleObserver import io.nekohasekai.sfa.utils.CommandClient +import java.util.concurrent.atomic.AtomicLong import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.isActive +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext data class ConnectionsUiState( @@ -35,70 +38,62 @@ sealed class ConnectionsEvent : ScreenEvent { data object AllConnectionsClosed : ConnectionsEvent() } -class ConnectionsViewModel( - private val sharedCommandClient: CommandClient? = null, -) : BaseViewModel(), CommandClient.Handler { - private val commandClient: CommandClient - private val isUsingSharedClient: Boolean +class ConnectionsViewModel : BaseViewModel(), CommandClient.Handler { + private val commandClient = CommandClient( + viewModelScope, + CommandClient.ConnectionType.Connections, + this, + ) private val _serviceStatus = MutableStateFlow(Status.Stopped) val serviceStatus = _serviceStatus.asStateFlow() private var lastServiceStatus: Status = Status.Stopped - private var connectionJob: Job? = null - private var rawConnections: Connections? = null + private val _isVisible = MutableStateFlow(false) - init { - if (sharedCommandClient != null) { - commandClient = sharedCommandClient - isUsingSharedClient = true - commandClient.addHandler(this) - } else { - commandClient = CommandClient( - viewModelScope, - CommandClient.ConnectionType.Connections, - this, - ) - isUsingSharedClient = false - } - } + private var connectionsStore: Connections? = null + private val connectionsMutex = Mutex() + private val connectionsGeneration = AtomicLong(0) override fun createInitialState() = ConnectionsUiState() - override fun onCleared() { - super.onCleared() - connectionJob?.cancel() - connectionJob = null - if (isUsingSharedClient) { - commandClient.removeHandler(this) - } else { - commandClient.disconnect() + init { + viewModelScope.launch { + combine( + AppLifecycleObserver.isForeground, + _isVisible, + _serviceStatus + ) { foreground, visible, status -> + Triple(foreground, visible, status) + }.collect { (foreground, visible, status) -> + val shouldConnect = foreground && visible && status == Status.Started + if (shouldConnect) { + updateState { copy(isLoading = true) } + commandClient.connect() + } else { + commandClient.disconnect() + } + } } } - private fun handleServiceStatusChange(status: Status) { - if (status == Status.Started) { - if (!isUsingSharedClient) { - updateState { copy(isLoading = true) } - connectionJob?.cancel() - connectionJob = viewModelScope.launch(Dispatchers.IO) { - while (isActive) { - try { - commandClient.connect() - break - } catch (e: Exception) { - delay(100) - } - } + fun setVisible(visible: Boolean) { + _isVisible.value = visible + } + + override fun onCleared() { + super.onCleared() + commandClient.disconnect() + } + + private suspend fun handleServiceStatusChange(status: Status) { + if (status != Status.Started) { + withContext(Dispatchers.Default) { + connectionsMutex.withLock { + connectionsStore = null } + connectionsGeneration.incrementAndGet() } - } else { - connectionJob?.cancel() - connectionJob = null - if (!isUsingSharedClient) { - commandClient.disconnect() - } - rawConnections = null updateState { copy(connections = emptyList(), allConnections = emptyList(), isLoading = false) } @@ -116,17 +111,17 @@ class ConnectionsViewModel( fun setStateFilter(filter: ConnectionStateFilter) { updateState { copy(stateFilter = filter) } - rawConnections?.let { processConnections(it) } + requestConnectionsRefresh() } fun setSort(sort: ConnectionSort) { updateState { copy(sort = sort) } - rawConnections?.let { processConnections(it) } + requestConnectionsRefresh() } fun setSearchText(text: String) { updateState { copy(searchText = text) } - rawConnections?.let { processConnections(it) } + requestConnectionsRefresh() } fun toggleSearch() { @@ -138,7 +133,7 @@ class ConnectionsViewModel( ) } if (!newSearchActive) { - rawConnections?.let { processConnections(it) } + requestConnectionsRefresh() } } @@ -175,50 +170,102 @@ class ConnectionsViewModel( } override fun onDisconnected() { - viewModelScope.launch(Dispatchers.Main) { - rawConnections = null - updateState { - copy(connections = emptyList(), allConnections = emptyList(), isLoading = false) + viewModelScope.launch(Dispatchers.Default) { + connectionsMutex.withLock { + connectionsStore = null + } + connectionsGeneration.incrementAndGet() + withContext(Dispatchers.Main) { + updateState { + copy(connections = emptyList(), allConnections = emptyList(), isLoading = false) + } } } } - override fun updateConnections(connections: Connections) { - rawConnections = connections - processConnections(connections) - } - - private fun processConnections(connections: Connections) { - connectionJob?.cancel() - connectionJob = viewModelScope.launch(Dispatchers.Default) { - val currentState = uiState.value - - val allConnectionList = connections.iterator().toList() - .filter { it.outboundType != "dns" } - .map { Connection.from(it) } - - connections.filterState(currentState.stateFilter.libboxValue) - - when (currentState.sort) { - ConnectionSort.ByDate -> connections.sortByDate() - ConnectionSort.ByTraffic -> connections.sortByTraffic() - ConnectionSort.ByTrafficTotal -> connections.sortByTrafficTotal() + override fun writeConnectionEvents(events: ConnectionEvents) { + viewModelScope.launch(Dispatchers.Default) { + val generation = connectionsGeneration.get() + val snapshot = connectionsMutex.withLock { + if (connectionsStore == null) { + connectionsStore = Libbox.newConnections() + } + val store = connectionsStore ?: return@withLock null + store.applyEvents(events) + buildConnectionLists(store, uiState.value) + } ?: return@launch + if (connectionsGeneration.get() != generation) { + return@launch } - - val connectionList = connections.iterator().toList() - .filter { it.outboundType != "dns" } - .map { Connection.from(it) } - .filter { it.performSearch(currentState.searchText) } - withContext(Dispatchers.Main) { + if (connectionsGeneration.get() != generation) { + return@withContext + } updateState { copy( - connections = connectionList, - allConnections = allConnectionList, + connections = snapshot.connections, + allConnections = snapshot.allConnections, isLoading = false, ) } } } } + + private fun requestConnectionsRefresh() { + viewModelScope.launch(Dispatchers.Default) { + val generation = connectionsGeneration.get() + val snapshot = connectionsMutex.withLock { + val store = connectionsStore ?: return@withLock null + buildConnectionLists(store, uiState.value) + } ?: return@launch + if (connectionsGeneration.get() != generation) { + return@launch + } + withContext(Dispatchers.Main) { + if (connectionsGeneration.get() != generation) { + return@withContext + } + updateState { + copy( + connections = snapshot.connections, + allConnections = snapshot.allConnections, + isLoading = false, + ) + } + } + } + } + + private fun buildConnectionLists( + connections: Connections, + currentState: ConnectionsUiState, + ): ConnectionLists { + val allConnectionList = connections.iterator().toList() + .filter { it.outboundType != "dns" } + .map { Connection.from(it) } + + connections.filterState(currentState.stateFilter.libboxValue) + + when (currentState.sort) { + ConnectionSort.ByDate -> connections.sortByDate() + ConnectionSort.ByTraffic -> connections.sortByTraffic() + ConnectionSort.ByTrafficTotal -> connections.sortByTrafficTotal() + } + + val connectionList = connections.iterator().toList() + .filter { it.outboundType != "dns" } + .map { Connection.from(it) } + .filter { it.performSearch(currentState.searchText) } + + return ConnectionLists( + connections = connectionList, + allConnections = allConnectionList, + ) + } + + private data class ConnectionLists( + val connections: List, + val allConnections: List, + ) } diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/DashboardViewModel.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/DashboardViewModel.kt index cbb8f24..2b58b55 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/DashboardViewModel.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/DashboardViewModel.kt @@ -1,11 +1,9 @@ package io.nekohasekai.sfa.compose.screen.dashboard import androidx.lifecycle.viewModelScope -import io.nekohasekai.libbox.Connections import io.nekohasekai.libbox.Libbox import io.nekohasekai.libbox.OutboundGroup import io.nekohasekai.libbox.StatusMessage -import io.nekohasekai.sfa.ktx.toList import io.nekohasekai.sfa.bg.BoxService import io.nekohasekai.sfa.compose.base.BaseViewModel import io.nekohasekai.sfa.compose.base.UiEvent @@ -14,6 +12,7 @@ import io.nekohasekai.sfa.database.Profile import io.nekohasekai.sfa.database.ProfileManager import io.nekohasekai.sfa.database.Settings import io.nekohasekai.sfa.database.TypedProfile +import io.nekohasekai.sfa.utils.AppLifecycleObserver import io.nekohasekai.sfa.utils.CommandClient import io.nekohasekai.sfa.utils.HTTPClient import kotlinx.coroutines.Dispatchers @@ -135,7 +134,6 @@ class DashboardViewModel : BaseViewModel(), CommandCl CommandClient.ConnectionType.Status, CommandClient.ConnectionType.ClashMode, CommandClient.ConnectionType.Groups, - CommandClient.ConnectionType.Connections, ), this, ) @@ -157,6 +155,17 @@ class DashboardViewModel : BaseViewModel(), CommandCl init { loadProfiles() ProfileManager.registerCallback(::onProfilesChanged) + + viewModelScope.launch { + AppLifecycleObserver.isForeground.collect { foreground -> + if (_serviceStatus.value != Status.Started) return@collect + if (foreground) { + commandClient.connect() + } else { + commandClient.disconnect() + } + } + } } override fun onCleared() { @@ -447,7 +456,9 @@ class DashboardViewModel : BaseViewModel(), CommandCl when (status) { Status.Started -> { checkDeprecatedNotes() - commandClient.connect() + if (AppLifecycleObserver.isForeground.value) { + commandClient.connect() + } reloadSystemProxyStatus() reloadStartedAt() } @@ -458,6 +469,7 @@ class DashboardViewModel : BaseViewModel(), CommandCl copy( hasGroups = false, groupsCount = 0, + connectionsCount = 0, serviceStartTime = null, clashModeVisible = false, systemProxyVisible = false, @@ -587,6 +599,7 @@ class DashboardViewModel : BaseViewModel(), CommandCl goroutines = status.goroutines.toString(), // Only set trafficVisible to true, never back to false from status updates trafficVisible = if (status.trafficAvailable) true else trafficVisible, + connectionsCount = status.connectionsIn, connectionsIn = status.connectionsIn.toString(), connectionsOut = status.connectionsOut.toString(), uplink = "${Libbox.formatBytes(status.uplink)}/s", @@ -633,13 +646,6 @@ class DashboardViewModel : BaseViewModel(), CommandCl } } - override fun updateConnections(connections: Connections) { - viewModelScope.launch(Dispatchers.Main) { - val count = connections.iterator().toList().count { it.outboundType != "dns" } - updateState { copy(connectionsCount = count) } - } - } - fun toggleCardSettingsDialog() { updateState { copy(showCardSettingsDialog = !showCardSettingsDialog) diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/groups/GroupsViewModel.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/groups/GroupsViewModel.kt index 5bc18c9..1151252 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/groups/GroupsViewModel.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/dashboard/groups/GroupsViewModel.kt @@ -9,13 +9,11 @@ import io.nekohasekai.sfa.constant.Status import io.nekohasekai.sfa.compose.model.Group import io.nekohasekai.sfa.compose.model.GroupItem import io.nekohasekai.sfa.compose.model.toList +import io.nekohasekai.sfa.utils.AppLifecycleObserver import io.nekohasekai.sfa.utils.CommandClient import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -39,7 +37,6 @@ class GroupsViewModel( private val _serviceStatus = MutableStateFlow(Status.Stopped) val serviceStatus = _serviceStatus.asStateFlow() private var lastServiceStatus: Status = Status.Stopped - private var connectionJob: Job? = null init { if (sharedCommandClient != null) { @@ -55,14 +52,32 @@ class GroupsViewModel( ) isUsingSharedClient = false } + + viewModelScope.launch { + AppLifecycleObserver.isForeground.collect { foreground -> + if (lastServiceStatus != Status.Started) return@collect + if (foreground) { + if (isUsingSharedClient) { + commandClient.addHandler(this@GroupsViewModel) + } else { + updateState { copy(isLoading = true) } + commandClient.connect() + } + } else { + if (isUsingSharedClient) { + commandClient.removeHandler(this@GroupsViewModel) + } else { + commandClient.disconnect() + } + } + } + } } override fun createInitialState() = GroupsUiState() override fun onCleared() { super.onCleared() - connectionJob?.cancel() - connectionJob = null if (isUsingSharedClient) { commandClient.removeHandler(this) } else { @@ -72,25 +87,11 @@ class GroupsViewModel( private fun handleServiceStatusChange(status: Status) { if (status == Status.Started) { - if (!isUsingSharedClient) { - updateState { - copy(isLoading = true) - } - connectionJob?.cancel() - connectionJob = viewModelScope.launch(Dispatchers.IO) { - while (isActive) { - try { - commandClient.connect() - break - } catch (e: Exception) { - delay(100) - } - } - } + if (!isUsingSharedClient && AppLifecycleObserver.isForeground.value) { + updateState { copy(isLoading = true) } + commandClient.connect() } } else { - connectionJob?.cancel() - connectionJob = null if (!isUsingSharedClient) { commandClient.disconnect() } @@ -243,8 +244,6 @@ class GroupsViewModel( } override fun updateGroups(newGroups: MutableList) { - connectionJob?.cancel() - connectionJob = null viewModelScope.launch(Dispatchers.Default) { val currentGroups = uiState.value.groups val newGroupsMap = newGroups.associateBy { it.tag } diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/log/LogViewModel.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/log/LogViewModel.kt index dd5e76d..4e885ba 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/log/LogViewModel.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/log/LogViewModel.kt @@ -5,6 +5,7 @@ import io.nekohasekai.libbox.Libbox import io.nekohasekai.libbox.LogEntry import io.nekohasekai.sfa.compose.util.AnsiColorUtils import io.nekohasekai.sfa.constant.Status +import io.nekohasekai.sfa.utils.AppLifecycleObserver import io.nekohasekai.sfa.utils.CommandClient import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.update @@ -24,6 +25,20 @@ class LogViewModel : BaseLogViewModel(), CommandClient.Handler { connectionType = CommandClient.ConnectionType.Log, handler = this, ) + private var lastServiceStatus: Status = Status.Stopped + + init { + viewModelScope.launch { + AppLifecycleObserver.isForeground.collect { foreground -> + if (lastServiceStatus != Status.Started) return@collect + if (foreground) { + commandClient.connect() + } else { + commandClient.disconnect() + } + } + } + } private fun processLogEntry(entry: LogEntry): ProcessedLogEntry { val level = LogLevel.entries.find { it.priority == entry.level } ?: LogLevel.Default @@ -35,11 +50,14 @@ class LogViewModel : BaseLogViewModel(), CommandClient.Handler { } override fun updateServiceStatus(status: Status) { + lastServiceStatus = status _uiState.update { it.copy(serviceStatus = status) } when (status) { Status.Started -> { - commandClient.connect() + if (AppLifecycleObserver.isForeground.value) { + commandClient.connect() + } } Status.Stopped, Status.Stopping -> { diff --git a/app/src/main/java/io/nekohasekai/sfa/utils/AppLifecycleObserver.kt b/app/src/main/java/io/nekohasekai/sfa/utils/AppLifecycleObserver.kt new file mode 100644 index 0000000..123bb12 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/utils/AppLifecycleObserver.kt @@ -0,0 +1,25 @@ +package io.nekohasekai.sfa.utils + +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ProcessLifecycleOwner +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +object AppLifecycleObserver : DefaultLifecycleObserver { + private val _isForeground = MutableStateFlow(true) + val isForeground: StateFlow = _isForeground.asStateFlow() + + fun register() { + ProcessLifecycleOwner.get().lifecycle.addObserver(this) + } + + override fun onStart(owner: LifecycleOwner) { + _isForeground.value = true + } + + override fun onStop(owner: LifecycleOwner) { + _isForeground.value = false + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/utils/CommandClient.kt b/app/src/main/java/io/nekohasekai/sfa/utils/CommandClient.kt index 7a9d2d6..606cffa 100644 --- a/app/src/main/java/io/nekohasekai/sfa/utils/CommandClient.kt +++ b/app/src/main/java/io/nekohasekai/sfa/utils/CommandClient.kt @@ -5,7 +5,7 @@ import go.Seq import io.nekohasekai.libbox.CommandClient import io.nekohasekai.libbox.CommandClientHandler import io.nekohasekai.libbox.CommandClientOptions -import io.nekohasekai.libbox.Connections +import io.nekohasekai.libbox.ConnectionEvents import io.nekohasekai.libbox.Libbox import io.nekohasekai.libbox.LogEntry import io.nekohasekai.libbox.LogIterator @@ -15,10 +15,6 @@ import io.nekohasekai.libbox.StatusMessage import io.nekohasekai.libbox.StringIterator import io.nekohasekai.sfa.ktx.toList import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch open class CommandClient( private val scope: CoroutineScope, @@ -87,7 +83,7 @@ open class CommandClient( fun updateClashMode(newMode: String) {} - fun updateConnections(connections: Connections) {} + fun writeConnectionEvents(events: ConnectionEvents) {} } private var commandClient: CommandClient? = null @@ -109,27 +105,8 @@ open class CommandClient( } options.statusInterval = 1 * 1000 * 1000 * 1000 val commandClient = CommandClient(clientHandler, options) - scope.launch(Dispatchers.IO) { - for (i in 1..10) { - delay(100 + i.toLong() * 50) - try { - commandClient.connect() - } catch (ignored: Exception) { - continue - } - if (!isActive) { - runCatching { - commandClient.disconnect() - } - return@launch - } - this@CommandClient.commandClient = commandClient - return@launch - } - runCatching { - commandClient.disconnect() - } - } + commandClient.connect() + this.commandClient = commandClient } fun disconnect() { @@ -197,9 +174,9 @@ open class CommandClient( getAllHandlers().forEach { it.updateClashMode(newMode) } } - override fun writeConnections(message: Connections?) { - if (message == null) return - getAllHandlers().forEach { it.updateConnections(message) } + override fun writeConnectionEvents(events: ConnectionEvents?) { + if (events == null) return + getAllHandlers().forEach { it.writeConnectionEvents(events) } } } }