diff options
Diffstat (limited to 'ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt')
-rw-r--r-- | ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt b/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt new file mode 100644 index 00000000..227c1291 --- /dev/null +++ b/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt @@ -0,0 +1,146 @@ +/* + * Copyright © 2017-2025 WireGuard LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +package com.wireguard.android.model + +import android.util.Log +import androidx.databinding.BaseObservable +import androidx.databinding.Bindable +import com.wireguard.android.BR +import com.wireguard.android.backend.Statistics +import com.wireguard.android.backend.Tunnel +import com.wireguard.android.databinding.Keyed +import com.wireguard.android.util.applicationScope +import com.wireguard.config.Config +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +/** + * Encapsulates the volatile and nonvolatile state of a WireGuard tunnel. + */ +class ObservableTunnel internal constructor( + private val manager: TunnelManager, + private var name: String, + config: Config?, + state: Tunnel.State +) : BaseObservable(), Keyed<String>, Tunnel { + override val key + get() = name + + @Bindable + override fun getName() = name + + suspend fun setNameAsync(name: String): String = withContext(Dispatchers.Main.immediate) { + if (name != this@ObservableTunnel.name) + manager.setTunnelName(this@ObservableTunnel, name) + else + this@ObservableTunnel.name + } + + fun onNameChanged(name: String): String { + this.name = name + notifyPropertyChanged(BR.name) + return name + } + + + @get:Bindable + var state = state + private set + + override fun onStateChange(newState: Tunnel.State) { + onStateChanged(newState) + } + + fun onStateChanged(state: Tunnel.State): Tunnel.State { + if (state != Tunnel.State.UP) onStatisticsChanged(null) + this.state = state + notifyPropertyChanged(BR.state) + return state + } + + suspend fun setStateAsync(state: Tunnel.State): Tunnel.State = withContext(Dispatchers.Main.immediate) { + if (state != this@ObservableTunnel.state) + manager.setTunnelState(this@ObservableTunnel, state) + else + this@ObservableTunnel.state + } + + + @get:Bindable + var config = config + get() { + if (field == null) + // Opportunistically fetch this if we don't have a cached one, and rely on data bindings to update it eventually + applicationScope.launch { + try { + manager.getTunnelConfig(this@ObservableTunnel) + } catch (e: Throwable) { + Log.e(TAG, Log.getStackTraceString(e)) + } + } + return field + } + private set + + suspend fun getConfigAsync(): Config = withContext(Dispatchers.Main.immediate) { + config ?: manager.getTunnelConfig(this@ObservableTunnel) + } + + suspend fun setConfigAsync(config: Config): Config = withContext(Dispatchers.Main.immediate) { + this@ObservableTunnel.config.let { + if (config != it) + manager.setTunnelConfig(this@ObservableTunnel, config) + else + it + } + } + + fun onConfigChanged(config: Config?): Config? { + this.config = config + notifyPropertyChanged(BR.config) + return config + } + + + @get:Bindable + var statistics: Statistics? = null + get() { + if (field == null || field?.isStale != false) + // Opportunistically fetch this if we don't have a cached one, and rely on data bindings to update it eventually + applicationScope.launch { + try { + manager.getTunnelStatistics(this@ObservableTunnel) + } catch (e: Throwable) { + Log.e(TAG, Log.getStackTraceString(e)) + } + } + return field + } + private set + + suspend fun getStatisticsAsync(): Statistics = withContext(Dispatchers.Main.immediate) { + statistics.let { + if (it == null || it.isStale) + manager.getTunnelStatistics(this@ObservableTunnel) + else + it + } + } + + fun onStatisticsChanged(statistics: Statistics?): Statistics? { + this.statistics = statistics + notifyPropertyChanged(BR.statistics) + return statistics + } + + + suspend fun deleteAsync() = manager.delete(this) + + + companion object { + private const val TAG = "WireGuard/ObservableTunnel" + } +} |