From b1b08ce716301d53701f14557f7d3edf0214ab26 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Mon, 24 Apr 2023 18:07:03 +0200 Subject: [PATCH] ui: display latest handshake time Signed-off-by: Jason A. Donenfeld --- .../android/fragment/TunnelDetailFragment.kt | 25 ++++++++--- .../android/util/QuantityFormatter.kt | 45 +++++++++++++++++++ ui/src/main/res/layout/tunnel_detail_peer.xml | 30 ++++++++++++- ui/src/main/res/values/strings.xml | 2 + 4 files changed, 94 insertions(+), 8 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 8b155e24..57e6828a 100644 --- a/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt +++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt @@ -112,16 +112,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,6 +138,8 @@ 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 } } } 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(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/res/layout/tunnel_detail_peer.xml b/ui/src/main/res/layout/tunnel_detail_peer.xml index ccbf27ef..25081cea 100644 --- a/ui/src/main/res/layout/tunnel_detail_peer.xml +++ b/ui/src/main/res/layout/tunnel_detail_peer.xml @@ -171,7 +171,7 @@ android:id="@+id/transfer_label" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_below="@+id/endpoint_text" + android:layout_below="@+id/persistent_keepalive_text" android:layout_marginTop="8dp" android:labelFor="@+id/transfer_text" android:text="@string/transfer" @@ -194,6 +194,34 @@ app:layout_constraintTop_toBottomOf="@+id/transfer_label" tools:text="1024 MB" tools:visibility="visible" /> + + + + diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index bc85bdbf..e842a393 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -140,6 +140,8 @@ : WireGuard base64 keys must be 44 characters (32 bytes) : WireGuard keys must be 32 bytes : WireGuard hex keys must be 64 characters (32 bytes) + Latest handshake + %s ago Listen port Unable to export log: %s WireGuard Android Log File