diff options
Diffstat (limited to 'ui')
5 files changed, 134 insertions, 23 deletions
diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt index 74b29ad1..53f79c1d 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt @@ -115,7 +115,7 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider { for (i in 0 until binding.peersLayout.childCount) { val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i)) ?: continue - val publicKey = peer.item!!.peer.publicKey + val publicKey = peer.item!!.publicKey val peerStats = statistics.peer(publicKey) if (peerStats == null || (peerStats.rxBytes == 0L && peerStats.txBytes == 0L)) { peer.transferLabel.visibility = View.GONE diff --git a/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt b/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt index d4a0b1c0..e38e473f 100644 --- a/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt +++ b/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt @@ -7,6 +7,7 @@ package com.wireguard.android.model import android.util.Log import androidx.databinding.BaseObservable import androidx.databinding.Bindable +import com.wireguard.android.Application import com.wireguard.android.BR import com.wireguard.android.backend.Dhcp import com.wireguard.android.backend.Statistics @@ -14,10 +15,12 @@ import com.wireguard.android.backend.Tunnel import com.wireguard.android.databinding.Keyed import com.wireguard.android.util.applicationScope import com.wireguard.android.viewmodel.ConfigDetail +import com.wireguard.android.viewmodel.PeerDetail import com.wireguard.config.Config import com.wireguard.config.InetEndpoint -import com.wireguard.crypto.Key; -import java.util.Optional; +import com.wireguard.config.InetNetwork +import com.wireguard.crypto.Key +import java.util.Optional import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -63,10 +66,12 @@ class ObservableTunnel internal constructor( if (state != Tunnel.State.UP) { onStatisticsChanged(null) onDhcpChanged(null) - onEndpointChange(null, null) + Application.getCoroutineScope().launch { + onPeersReset() + } } else { configDetail?.peers?.forEach { - var endpoint: InetEndpoint? = it.peer.endpoint.orElse(null) + var endpoint: InetEndpoint? = it.peer?.endpoint?.orElse(null) it.endpoint = Optional.ofNullable(endpoint?.getResolved()?.get()) } } @@ -118,8 +123,8 @@ class ObservableTunnel internal constructor( } fun onConfigChanged(config: Config?): ConfigDetail? { - this.config = config this.configDetail = ConfigDetail(config) + this.config = config notifyPropertyChanged(BR.config) return configDetail } @@ -171,18 +176,89 @@ class ObservableTunnel internal constructor( return dhcp } - override fun onEndpointChange(publicKey: Key?, newEndpoint: InetEndpoint?) { + // Remove dynamic peers, and reset static peers + fun onPeersReset() { + Log.i(TAG, "ObservableTunnel onPeersReset") + var toRemove: MutableList<PeerDetail> = ArrayList() + + configDetail?.peers?.forEach { + if (it.peer == null) { + toRemove.add(it) + } else { + it.endpoint = Optional.empty() + } + } + + toRemove.forEach { + Log.i(TAG, "ObservableTunnel remove " + it) + configDetail?.peers?.remove(it) + } + } + + override fun onEndpointChange(publicKey: Key, newEndpoint: InetEndpoint?) { + Application.getCoroutineScope().launch { + onEndpointChanged(publicKey, newEndpoint) + } + } + + private fun onEndpointChanged(publicKey: Key, newEndpoint: InetEndpoint?) { + Log.i(TAG, "ObservableTunnel onEndpointChange " + newEndpoint) + var peer: PeerDetail? = null + configDetail?.peers?.forEach { - Log.i(TAG, "ObservableTunnel peer " + it + ", " + it.peer) - if (publicKey == null || it.peer.publicKey.equals(publicKey)) { - if (newEndpoint == null) { - it.endpoint = it.peer.endpoint - } else { - it.endpoint = newEndpoint.getResolved() - } + if (it.publicKey.equals(publicKey) == true) { + Log.i(TAG, "ObservableTunnel peer " + it + ", " + it.peer) + peer = it; + } + } + + if (peer == null) { + Log.i(TAG, "ObservableTunnel create peer " + publicKey) + peer = PeerDetail(publicKey) + configDetail?.peers?.add(peer) + } + + var peer2: PeerDetail = peer!! + + if (newEndpoint != null) { + peer2.endpoint = newEndpoint.getResolved() + } else { + var peer3 = peer2.peer + peer2.endpoint = if (peer3 != null) peer3.endpoint else Optional.empty() + } + } + + fun lookupPeer(publicKey: Key): PeerDetail { + configDetail?.peers?.forEach { + if (it.publicKey.equals(publicKey) == true) { + Log.i(TAG, "ObservableTunnel peer " + it + ", " + it.peer) + return it } } + + Log.i(TAG, "ObservableTunnel create peer " + publicKey) + var peer: PeerDetail = PeerDetail(publicKey) + configDetail?.peers?.add(peer) + + return peer + } + + override fun onAllowedIpsChange(publicKey: Key, addNetworks: List<InetNetwork>?, removeNetworks: List<InetNetwork>?) { + Application.getCoroutineScope().launch { + onAllowedIpsChanged(publicKey, addNetworks, removeNetworks) + } + } + + private fun onAllowedIpsChanged(publicKey: Key, addNetworks: List<InetNetwork>?, removeNetworks: List<InetNetwork>?) { + var peer: PeerDetail = lookupPeer(publicKey) + + removeNetworks?.let() { + peer.allowedIps.removeAll(removeNetworks) + } + addNetworks?.let() { + peer.allowedIps.addAll(addNetworks) + } } diff --git a/ui/src/main/java/com/wireguard/android/viewmodel/PeerDetail.kt b/ui/src/main/java/com/wireguard/android/viewmodel/PeerDetail.kt index abc81998..80b32fd5 100644 --- a/ui/src/main/java/com/wireguard/android/viewmodel/PeerDetail.kt +++ b/ui/src/main/java/com/wireguard/android/viewmodel/PeerDetail.kt @@ -4,25 +4,37 @@ import android.util.Log import androidx.databinding.BaseObservable import androidx.databinding.Bindable import androidx.databinding.Observable +import androidx.databinding.ObservableList +import androidx.databinding.ObservableArrayList import com.wireguard.android.BR import com.wireguard.config.InetEndpoint +import com.wireguard.config.InetNetwork import com.wireguard.config.Peer +import com.wireguard.crypto.Key; import java.util.Optional; +import kotlin.collections.LinkedHashSet + class PeerDetail : BaseObservable { - var peer: Peer + var peer: Peer? private var owner: ConfigDetail? = null @get:Bindable + var publicKey: Key + + @get:Bindable + var allowedIps: ObservableList<InetNetwork> = ObservableArrayList<InetNetwork>() + + @get:Bindable var endpoint: Optional<InetEndpoint> = Optional.empty() get() { if (!field.isEmpty()) { return field } else { - return peer.endpoint + return Optional.ofNullable(peer?.endpoint?.get()) } } @@ -32,8 +44,20 @@ class PeerDetail : BaseObservable { notifyPropertyChanged(BR.endpoint) } + @get:Bindable + var persistentKeepalive: Optional<Int> = Optional.empty() + constructor(other: Peer) { peer = other + publicKey = other.getPublicKey() + allowedIps.addAll(other.getAllowedIps()) + endpoint = other.getEndpoint(); + persistentKeepalive = other.getPersistentKeepalive() + } + + constructor(publicKey: Key) { + peer = null + this.publicKey = publicKey } fun bind(owner: ConfigDetail) { @@ -45,6 +69,16 @@ class PeerDetail : BaseObservable { super.addOnPropertyChangedCallback(callback) } + /** + * Converts the {@code Peer} into a string suitable for debugging purposes. The {@code Peer} is + * identified by its public key and (if known) its endpoint. + * + * @return a concise single-line identifier for the {@code Peer} + */ + override fun toString(): String { + return "(Peer " + publicKey.toBase64() + ")" + } + companion object { private const val TAG = "WireGuard/PeerDetail" } diff --git a/ui/src/main/res/layout/tunnel_detail_fragment.xml b/ui/src/main/res/layout/tunnel_detail_fragment.xml index e1d026ff..08efc8f6 100644 --- a/ui/src/main/res/layout/tunnel_detail_fragment.xml +++ b/ui/src/main/res/layout/tunnel_detail_fragment.xml @@ -367,6 +367,7 @@ android:layout_marginTop="8dp" android:divider="@null" android:orientation="vertical" + app:fragment="@{fragment}" app:items="@{config.peers}" app:layout="@{@layout/tunnel_detail_peer}" app:layout_constraintStart_toStartOf="parent" diff --git a/ui/src/main/res/layout/tunnel_detail_peer.xml b/ui/src/main/res/layout/tunnel_detail_peer.xml index e443d300..89bb85ec 100644 --- a/ui/src/main/res/layout/tunnel_detail_peer.xml +++ b/ui/src/main/res/layout/tunnel_detail_peer.xml @@ -51,7 +51,7 @@ android:nextFocusForward="@id/pre_shared_key_text" android:onClick="@{ClipboardUtils::copyTextView}" android:singleLine="true" - android:text="@{item.peer.publicKey.toBase64}" + android:text="@{item.publicKey.toBase64}" android:textAppearance="?attr/textAppearanceBodyLarge" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/public_key_label" @@ -93,7 +93,7 @@ android:layout_marginTop="8dp" android:labelFor="@+id/allowed_ips_text" android:text="@string/allowed_ips" - android:visibility="@{item.peer.allowedIps.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}" + android:visibility="@{item.allowedIps.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/pre_shared_key_text" /> @@ -106,9 +106,9 @@ android:nextFocusDown="@id/endpoint_text" android:nextFocusForward="@id/endpoint_text" android:onClick="@{ClipboardUtils::copyTextView}" - android:text="@{item.peer.allowedIps}" + android:text="@{item.allowedIps}" android:textAppearance="?attr/textAppearanceBodyLarge" - android:visibility="@{item.peer.allowedIps.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}" + android:visibility="@{item.allowedIps.isEmpty() ? android.view.View.GONE : android.view.View.VISIBLE}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/allowed_ips_label" tools:text="0.0.0.0/5, 8.0.0.0/7, 11.0.0.0/8, 12.0.0.0/6, 16.0.0.0/4, 32.0.0.0/3" /> @@ -147,7 +147,7 @@ android:layout_marginTop="8dp" android:labelFor="@+id/persistent_keepalive_text" android:text="@string/persistent_keepalive" - android:visibility="@{!item.peer.persistentKeepalive.isPresent() ? android.view.View.GONE : android.view.View.VISIBLE}" + android:visibility="@{!item.persistentKeepalive.isPresent() ? android.view.View.GONE : android.view.View.VISIBLE}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/endpoint_text" /> @@ -160,9 +160,9 @@ android:nextFocusDown="@id/transfer_text" android:nextFocusForward="@id/transfer_text" android:onClick="@{ClipboardUtils::copyTextView}" - android:text="@{@plurals/persistent_keepalive_seconds_unit(item.peer.persistentKeepalive.orElse(0), item.peer.persistentKeepalive.orElse(0))}" + android:text="@{@plurals/persistent_keepalive_seconds_unit(item.persistentKeepalive.orElse(0), item.persistentKeepalive.orElse(0))}" android:textAppearance="?attr/textAppearanceBodyLarge" - android:visibility="@{!item.peer.persistentKeepalive.isPresent() ? android.view.View.GONE : android.view.View.VISIBLE}" + android:visibility="@{!item.persistentKeepalive.isPresent() ? android.view.View.GONE : android.view.View.VISIBLE}" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/persistent_keepalive_label" tools:text="every 3 seconds" /> |