diff options
Diffstat (limited to 'ui/src/main/java/com')
10 files changed, 413 insertions, 16 deletions
diff --git a/ui/src/main/java/com/wireguard/android/activity/LogViewerActivity.kt b/ui/src/main/java/com/wireguard/android/activity/LogViewerActivity.kt index 36925bea..9deed440 100644 --- a/ui/src/main/java/com/wireguard/android/activity/LogViewerActivity.kt +++ b/ui/src/main/java/com/wireguard/android/activity/LogViewerActivity.kt @@ -157,8 +157,7 @@ class LogViewerActivity : AppCompatActivity() { builder.append(rawLogLines[i]) builder.append('\n') } - val ret = builder.toString().toByteArray(Charsets.UTF_8) - return ret + return builder.toString().toByteArray(Charsets.UTF_8) } private suspend fun saveLog() { 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 8b155e24..53f79c1d 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt @@ -5,6 +5,7 @@ package com.wireguard.android.fragment import android.os.Bundle +import android.util.Log; import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -21,6 +22,7 @@ import com.wireguard.android.databinding.TunnelDetailFragmentBinding import com.wireguard.android.databinding.TunnelDetailPeerBinding import com.wireguard.android.model.ObservableTunnel import com.wireguard.android.util.QuantityFormatter +import com.wireguard.android.viewmodel.ConfigDetail import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -77,7 +79,9 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider { } else { lifecycleScope.launch { try { - binding.config = newTunnel.getConfigAsync() + var config = newTunnel.getConfigDetailAsync() + binding.config = config + Log.i(TAG, "onSelectedTunnelChanged " + config + ", " + config.config) } catch (_: Throwable) { binding.config = null } @@ -112,16 +116,25 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider { val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i)) ?: continue val publicKey = peer.item!!.publicKey - val rx = statistics.peerRx(publicKey) - val tx = statistics.peerTx(publicKey) - if (rx == 0L && tx == 0L) { + val peerStats = statistics.peer(publicKey) + if (peerStats == null || (peerStats.rxBytes == 0L && peerStats.txBytes == 0L)) { peer.transferLabel.visibility = View.GONE peer.transferText.visibility = View.GONE - continue + } else { + peer.transferText.text = getString(R.string.transfer_rx_tx, + QuantityFormatter.formatBytes(peerStats.rxBytes), + QuantityFormatter.formatBytes(peerStats.txBytes)) + peer.transferLabel.visibility = View.VISIBLE + peer.transferText.visibility = View.VISIBLE + } + if (peerStats == null || peerStats.latestHandshakeEpochMillis == 0L) { + peer.latestHandshakeLabel.visibility = View.GONE + peer.latestHandshakeText.visibility = View.GONE + } else { + peer.latestHandshakeText.text = QuantityFormatter.formatEpochAgo(peerStats.latestHandshakeEpochMillis) + peer.latestHandshakeLabel.visibility = View.VISIBLE + peer.latestHandshakeText.visibility = View.VISIBLE } - peer.transferText.text = getString(R.string.transfer_rx_tx, QuantityFormatter.formatBytes(rx), QuantityFormatter.formatBytes(tx)) - peer.transferLabel.visibility = View.VISIBLE - peer.transferText.visibility = View.VISIBLE } } catch (e: Throwable) { for (i in 0 until binding.peersLayout.childCount) { @@ -129,7 +142,13 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider { ?: continue peer.transferLabel.visibility = View.GONE peer.transferText.visibility = View.GONE + peer.latestHandshakeLabel.visibility = View.GONE + peer.latestHandshakeText.visibility = View.GONE } } } + + companion object { + private const val TAG = "WireGuard/TunnelDetailFragment" + } } diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt index 7a8b822e..9597fef6 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt @@ -17,13 +17,17 @@ import android.view.View import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.InputMethodManager +import android.widget.ArrayAdapter +import android.widget.AutoCompleteTextView import android.widget.EditText +import android.widget.Filter import android.widget.Toast import androidx.core.os.BundleCompat import androidx.core.view.MenuProvider import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textfield.TextInputLayout import com.wireguard.android.Application import com.wireguard.android.R import com.wireguard.android.backend.Tunnel @@ -33,6 +37,7 @@ import com.wireguard.android.util.AdminKnobs import com.wireguard.android.util.BiometricAuthenticator import com.wireguard.android.util.ErrorMessages import com.wireguard.android.viewmodel.ConfigProxy +import com.wireguard.android.viewmodel.Constants import com.wireguard.config.Config import kotlinx.coroutines.launch @@ -44,6 +49,21 @@ class TunnelEditorFragment : BaseFragment(), MenuProvider { private var binding: TunnelEditorFragmentBinding? = null private var tunnel: ObservableTunnel? = null + private class MaterialSpinnerAdapter<T>(context: Context, resource: Int, private val objects: List<T>) : ArrayAdapter<T>(context, resource, objects) { + private val _filter: Filter by lazy { + object : Filter() { + override fun performFiltering(constraint: CharSequence?): FilterResults { + return FilterResults() + } + + override fun publishResults(constraint: CharSequence?, results: FilterResults?) { + } + } + } + + override fun getFilter(): Filter = _filter + } + private fun onConfigLoaded(config: Config) { binding?.config = ConfigProxy(config) } @@ -83,6 +103,13 @@ class TunnelEditorFragment : BaseFragment(), MenuProvider { executePendingBindings() privateKeyTextLayout.setEndIconOnClickListener { config?.`interface`?.generateKeyPair() } } + + var httpProxyMenu = binding?.root?.findViewById<TextInputLayout>(R.id.http_proxy_menu) + var httpProxyItems = listOf(Constants.HTTP_PROXY_NONE, Constants.HTTP_PROXY_MANUAL, Constants.HTTP_PROXY_PAC) + var httpProxyAdapter = MaterialSpinnerAdapter(requireContext(), R.layout.http_proxy_menu_item, httpProxyItems) + var httpProxyMenuText = httpProxyMenu?.editText as? AutoCompleteTextView + httpProxyMenuText?.setAdapter(httpProxyAdapter) + return binding?.root } 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 c3e3405e..490a2216 100644 --- a/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt +++ b/ui/src/main/java/com/wireguard/android/model/ObservableTunnel.kt @@ -7,12 +7,20 @@ 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 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.config.InetNetwork +import com.wireguard.crypto.Key +import java.util.Optional import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -55,7 +63,18 @@ class ObservableTunnel internal constructor( } fun onStateChanged(state: Tunnel.State): Tunnel.State { - if (state != Tunnel.State.UP) onStatisticsChanged(null) + if (state != Tunnel.State.UP) { + onStatisticsChanged(null) + onDhcpChanged(null) + Application.getCoroutineScope().launch { + onPeersReset() + } + } else { + configDetail?.peers?.forEach { + var endpoint: InetEndpoint? = it.peer?.endpoint?.orElse(null) + it.endpoint = Optional.ofNullable(endpoint?.getResolved()?.orElse(null)); + } + } this.state = state notifyPropertyChanged(BR.state) return state @@ -68,6 +87,7 @@ class ObservableTunnel internal constructor( this@ObservableTunnel.state } + private var configDetail: ConfigDetail? = if (config != null) ConfigDetail(config) else null @get:Bindable var config = config @@ -86,7 +106,11 @@ class ObservableTunnel internal constructor( private set suspend fun getConfigAsync(): Config = withContext(Dispatchers.Main.immediate) { - config ?: manager.getTunnelConfig(this@ObservableTunnel) + config ?: manager.getTunnelConfig(this@ObservableTunnel).config!! + } + + suspend fun getConfigDetailAsync(): ConfigDetail = withContext(Dispatchers.Main.immediate) { + configDetail ?: manager.getTunnelConfig(this@ObservableTunnel) } suspend fun setConfigAsync(config: Config): Config = withContext(Dispatchers.Main.immediate) { @@ -98,10 +122,11 @@ class ObservableTunnel internal constructor( } } - fun onConfigChanged(config: Config?): Config? { + fun onConfigChanged(config: Config?): ConfigDetail? { + this.configDetail = ConfigDetail(config) this.config = config notifyPropertyChanged(BR.config) - return config + return configDetail } @@ -137,6 +162,106 @@ class ObservableTunnel internal constructor( } + @get:Bindable + var dhcp: Dhcp? = null + private set + + override fun onDhcpChange(newDhcp: Dhcp) { + onDhcpChanged(newDhcp) + } + + fun onDhcpChanged(dhcp: Dhcp?): Dhcp? { + this.dhcp = dhcp + notifyPropertyChanged(BR.dhcp) + return dhcp + } + + // 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 { + 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) + } + } + + suspend fun deleteAsync() = manager.delete(this) diff --git a/ui/src/main/java/com/wireguard/android/model/TunnelManager.kt b/ui/src/main/java/com/wireguard/android/model/TunnelManager.kt index e7bb751b..77d69f70 100644 --- a/ui/src/main/java/com/wireguard/android/model/TunnelManager.kt +++ b/ui/src/main/java/com/wireguard/android/model/TunnelManager.kt @@ -24,6 +24,7 @@ import com.wireguard.android.databinding.ObservableSortedKeyedArrayList import com.wireguard.android.util.ErrorMessages import com.wireguard.android.util.UserKnobs import com.wireguard.android.util.applicationScope +import com.wireguard.android.viewmodel.ConfigDetail import com.wireguard.config.Config import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers @@ -94,7 +95,7 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() { applicationScope.launch { UserKnobs.setLastUsedTunnel(value?.name) } } - suspend fun getTunnelConfig(tunnel: ObservableTunnel): Config = withContext(Dispatchers.Main.immediate) { + suspend fun getTunnelConfig(tunnel: ObservableTunnel): ConfigDetail = withContext(Dispatchers.Main.immediate) { tunnel.onConfigChanged(withContext(Dispatchers.IO) { configStore.load(tunnel.name) })!! } @@ -155,7 +156,7 @@ class TunnelManager(private val configStore: ConfigStore) : BaseObservable() { tunnel.onConfigChanged(withContext(Dispatchers.IO) { getBackend().setState(tunnel, tunnel.state, config) configStore.save(tunnel.name, config) - })!! + })!!.config!! } suspend fun setTunnelName(tunnel: ObservableTunnel, name: String): String = withContext(Dispatchers.Main.immediate) { diff --git a/ui/src/main/java/com/wireguard/android/util/ErrorMessages.kt b/ui/src/main/java/com/wireguard/android/util/ErrorMessages.kt index 66027d95..8df5eb0d 100644 --- a/ui/src/main/java/com/wireguard/android/util/ErrorMessages.kt +++ b/ui/src/main/java/com/wireguard/android/util/ErrorMessages.kt @@ -114,6 +114,8 @@ object ErrorMessages { return resources.getString(R.string.bad_config_explanation_udp_port) } else if (bce.location == BadConfigException.Location.MTU) { return resources.getString(R.string.bad_config_explanation_positive_number) + } else if (bce.location == BadConfigException.Location.HTTP_PROXY) { + return resources.getString(R.string.bad_config_explanation_http_proxy) } else if (bce.location == BadConfigException.Location.PERSISTENT_KEEPALIVE) { return resources.getString(R.string.bad_config_explanation_pka) } diff --git a/ui/src/main/java/com/wireguard/android/util/QuantityFormatter.kt b/ui/src/main/java/com/wireguard/android/util/QuantityFormatter.kt index 974fe530..5a533550 100644 --- a/ui/src/main/java/com/wireguard/android/util/QuantityFormatter.kt +++ b/ui/src/main/java/com/wireguard/android/util/QuantityFormatter.kt @@ -5,8 +5,17 @@ package com.wireguard.android.util +import android.icu.text.ListFormatter +import android.icu.text.MeasureFormat +import android.icu.text.RelativeDateTimeFormatter +import android.icu.util.Measure +import android.icu.util.MeasureUnit +import android.os.Build import com.wireguard.android.Application import com.wireguard.android.R +import java.util.Locale +import kotlin.time.DurationUnit +import kotlin.time.toDuration object QuantityFormatter { fun formatBytes(bytes: Long): String { @@ -19,4 +28,40 @@ object QuantityFormatter { else -> context.getString(R.string.transfer_tibibytes, bytes / (1024.0 * 1024.0 * 1024.0) / 1024.0) } } + + fun formatEpochAgo(epochMillis: Long): String { + var span = (System.currentTimeMillis() - epochMillis) / 1000 + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) + return Application.get().applicationContext.getString(R.string.latest_handshake_ago, span.toDuration(DurationUnit.SECONDS).toString()) + + if (span <= 0L) + return RelativeDateTimeFormatter.getInstance().format(RelativeDateTimeFormatter.Direction.PLAIN, RelativeDateTimeFormatter.AbsoluteUnit.NOW) + val measureFormat = MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE) + val parts = ArrayList<CharSequence>(4) + if (span >= 24 * 60 * 60L) { + val v = span / (24 * 60 * 60L) + parts.add(measureFormat.format(Measure(v, MeasureUnit.DAY))) + span -= v * (24 * 60 * 60L) + } + if (span >= 60 * 60L) { + val v = span / (60 * 60L) + parts.add(measureFormat.format(Measure(v, MeasureUnit.HOUR))) + span -= v * (60 * 60L) + } + if (span >= 60L) { + val v = span / 60L + parts.add(measureFormat.format(Measure(v, MeasureUnit.MINUTE))) + span -= v * 60L + } + if (span > 0L) + parts.add(measureFormat.format(Measure(span, MeasureUnit.SECOND))) + + val joined = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) + parts.joinToString() + else + ListFormatter.getInstance(Locale.getDefault(), ListFormatter.Type.UNITS, ListFormatter.Width.SHORT).format(parts) + + return Application.get().applicationContext.getString(R.string.latest_handshake_ago, joined) + } }
\ No newline at end of file diff --git a/ui/src/main/java/com/wireguard/android/viewmodel/ConfigDetail.kt b/ui/src/main/java/com/wireguard/android/viewmodel/ConfigDetail.kt new file mode 100644 index 00000000..af95a86a --- /dev/null +++ b/ui/src/main/java/com/wireguard/android/viewmodel/ConfigDetail.kt @@ -0,0 +1,22 @@ +package com.wireguard.android.viewmodel + +import androidx.databinding.ObservableArrayList +import androidx.databinding.ObservableList + +import com.wireguard.config.Config + +class ConfigDetail { + val config: Config? + val peers: ObservableList<PeerDetail> = ObservableArrayList() + + constructor(other: Config?) { + config = other + if (other != null) { + other.peers.forEach { + val detail = PeerDetail(it) + peers.add(detail) + detail.bind(this) + } + } + } +} diff --git a/ui/src/main/java/com/wireguard/android/viewmodel/InterfaceProxy.kt b/ui/src/main/java/com/wireguard/android/viewmodel/InterfaceProxy.kt index 004ebed1..16c3e6a3 100644 --- a/ui/src/main/java/com/wireguard/android/viewmodel/InterfaceProxy.kt +++ b/ui/src/main/java/com/wireguard/android/viewmodel/InterfaceProxy.kt @@ -4,6 +4,8 @@ */ package com.wireguard.android.viewmodel +import android.net.Uri +import android.os.Build import android.os.Parcel import android.os.Parcelable import androidx.databinding.BaseObservable @@ -18,6 +20,12 @@ import com.wireguard.crypto.Key import com.wireguard.crypto.KeyFormatException import com.wireguard.crypto.KeyPair +object Constants { + const val HTTP_PROXY_NONE = "None" + const val HTTP_PROXY_MANUAL = "Manual" + const val HTTP_PROXY_PAC = "Proxy Auto-Config" +} + class InterfaceProxy : BaseObservable, Parcelable { @get:Bindable val excludedApplications: ObservableList<String> = ObservableArrayList() @@ -54,6 +62,44 @@ class InterfaceProxy : BaseObservable, Parcelable { } @get:Bindable + var httpProxyMenu: String = "" + set(value) { + field = value + notifyPropertyChanged(BR.httpProxyMenu) + notifyPropertyChanged(BR.httpProxyManualVisibility) + notifyPropertyChanged(BR.httpProxyPacVisibility) + } + + @get:Bindable + var httpProxyManualVisibility: Int = 0 + get() = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) android.view.View.GONE else (if (httpProxyMenu == Constants.HTTP_PROXY_MANUAL) android.view.View.VISIBLE else android.view.View.GONE) + + @get:Bindable + var httpProxyHostname: String = "" + set(value) { + field = value + notifyPropertyChanged(BR.httpProxyHostname) + } + + @get:Bindable + var httpProxyPort: String = "" + set(value) { + field = value + notifyPropertyChanged(BR.httpProxyPort) + } + + @get:Bindable + var httpProxyPac: String = "" + set(value) { + field = value + notifyPropertyChanged(BR.httpProxyPac) + } + + @get:Bindable + var httpProxyPacVisibility: Int = 0 + get() = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) android.view.View.GONE else (if (httpProxyMenu == Constants.HTTP_PROXY_PAC) android.view.View.VISIBLE else android.view.View.GONE) + + @get:Bindable var privateKey: String = "" set(value) { field = value @@ -76,6 +122,10 @@ class InterfaceProxy : BaseObservable, Parcelable { parcel.readStringList(includedApplications) listenPort = parcel.readString() ?: "" mtu = parcel.readString() ?: "" + httpProxyMenu = parcel.readString() ?: "" + httpProxyHostname = parcel.readString() ?: "" + httpProxyPort = parcel.readString() ?: "" + httpProxyPac = parcel.readString() ?: "" privateKey = parcel.readString() ?: "" } @@ -87,6 +137,10 @@ class InterfaceProxy : BaseObservable, Parcelable { includedApplications.addAll(other.includedApplications) listenPort = other.listenPort.map { it.toString() }.orElse("") mtu = other.mtu.map { it.toString() }.orElse("") + httpProxyHostname = other.httpProxy.map { if (it.getHost().startsWith('[') && it.getHost().endsWith(']')) it.getHost().substring(1, it.getHost().length-1) else it.getHost() }.orElse("") + httpProxyPort = other.httpProxy.map { if (it.getPort() <= 0) "8080" else it.getPort().toString() }.orElse("") + httpProxyPac = other.httpProxy.map { it.getPacFileUrl().toString() }.orElse("") + httpProxyMenu = other.httpProxy.map { if (it.getPacFileUrl() != null && it.getPacFileUrl() != Uri.EMPTY) Constants.HTTP_PROXY_PAC else if (it.getHost() != "") Constants.HTTP_PROXY_MANUAL else Constants.HTTP_PROXY_NONE }.orElse(Constants.HTTP_PROXY_NONE) val keyPair = other.keyPair privateKey = keyPair.privateKey.toBase64() } @@ -111,6 +165,20 @@ class InterfaceProxy : BaseObservable, Parcelable { if (includedApplications.isNotEmpty()) builder.includeApplications(includedApplications) if (listenPort.isNotEmpty()) builder.parseListenPort(listenPort) if (mtu.isNotEmpty()) builder.parseMtu(mtu) + if (Constants.HTTP_PROXY_MANUAL.equals(httpProxyMenu) && httpProxyHostname.isNotEmpty()) { + var httpProxy: String + if (httpProxyHostname.contains(":")) { + httpProxy = "[" + httpProxyHostname + "]" + } else { + httpProxy = httpProxyHostname + } + if (httpProxyPort.isNotEmpty()) { + httpProxy += ":" + httpProxyPort; + } + builder.parseHttpProxy(httpProxy) + } else if (Constants.HTTP_PROXY_PAC.equals(httpProxyMenu) && httpProxyPac.isNotEmpty()) { + builder.parseHttpProxy("pac:" + httpProxyPac) + } if (privateKey.isNotEmpty()) builder.parsePrivateKey(privateKey) return builder.build() } @@ -122,6 +190,10 @@ class InterfaceProxy : BaseObservable, Parcelable { dest.writeStringList(includedApplications) dest.writeString(listenPort) dest.writeString(mtu) + dest.writeString(httpProxyMenu) + dest.writeString(httpProxyHostname) + dest.writeString(httpProxyPort) + dest.writeString(httpProxyPac) dest.writeString(privateKey) } diff --git a/ui/src/main/java/com/wireguard/android/viewmodel/PeerDetail.kt b/ui/src/main/java/com/wireguard/android/viewmodel/PeerDetail.kt new file mode 100644 index 00000000..80b32fd5 --- /dev/null +++ b/ui/src/main/java/com/wireguard/android/viewmodel/PeerDetail.kt @@ -0,0 +1,85 @@ +package com.wireguard.android.viewmodel + +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? + 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 Optional.ofNullable(peer?.endpoint?.get()) + } + } + + set(value) { + Log.i(TAG, "notifyPropertyChanged endpoint " + this + ", " + value) + field = value + 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) { + this.owner = owner + } + + override fun addOnPropertyChangedCallback (callback: Observable.OnPropertyChangedCallback) { + Log.i(TAG, "addOnPropertyChangedCallback " + this + ", " + callback) + 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" + } +} |