diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2023-04-24 18:07:03 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2023-04-24 18:07:03 +0200 |
commit | b1b08ce716301d53701f14557f7d3edf0214ab26 (patch) | |
tree | d31551784d124053683a370b97a30cc97d7933fc | |
parent | 20480992c4514325b4233c7ae8efd3e806b46081 (diff) |
ui: display latest handshake time
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
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<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/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" /> + + <TextView + android:id="@+id/latest_handshake_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/transfer_text" + android:layout_marginTop="8dp" + android:labelFor="@+id/latest_handshake_text" + android:text="@string/latest_handshake" + android:visibility="gone" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/transfer_text" + tools:visibility="visible" /> + + <TextView + android:id="@+id/latest_handshake_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/latest_handshake_label" + android:contentDescription="@string/latest_handshake" + android:nextFocusUp="@id/transfer_text" + android:onClick="@{ClipboardUtils::copyTextView}" + android:textAppearance="?attr/textAppearanceBodyLarge" + android:visibility="gone" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/latest_handshake_label" + tools:text="4 minutes, 27 seconds ago" + tools:visibility="visible" /> </androidx.constraintlayout.widget.ConstraintLayout> </com.google.android.material.card.MaterialCardView> </layout> 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 @@ <string name="key_length_explanation_base64">: WireGuard base64 keys must be 44 characters (32 bytes)</string> <string name="key_length_explanation_binary">: WireGuard keys must be 32 bytes</string> <string name="key_length_explanation_hex">: WireGuard hex keys must be 64 characters (32 bytes)</string> + <string name="latest_handshake">Latest handshake</string> + <string name="latest_handshake_ago">%s ago</string> <string name="listen_port">Listen port</string> <string name="log_export_error">Unable to export log: %s</string> <string name="log_export_subject">WireGuard Android Log File</string> |