Skip to content

Commit 16895ea

Browse files
committed
ui: display latest handshake time
Signed-off-by: Jason A. Donenfeld <[email protected]>
1 parent 487ef34 commit 16895ea

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,23 +112,34 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider {
112112
val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i))
113113
?: continue
114114
val publicKey = peer.item!!.publicKey
115-
val rx = statistics.peerRx(publicKey)
116-
val tx = statistics.peerTx(publicKey)
117-
if (rx == 0L && tx == 0L) {
115+
val peerStats = statistics.peer(publicKey)
116+
if (peerStats == null || (peerStats.rxBytes == 0L && peerStats.txBytes == 0L)) {
118117
peer.transferLabel.visibility = View.GONE
119118
peer.transferText.visibility = View.GONE
120-
continue
119+
} else {
120+
peer.transferText.text = getString(R.string.transfer_rx_tx,
121+
QuantityFormatter.formatBytes(peerStats.rxBytes),
122+
QuantityFormatter.formatBytes(peerStats.txBytes))
123+
peer.transferLabel.visibility = View.VISIBLE
124+
peer.transferText.visibility = View.VISIBLE
125+
}
126+
if (peerStats == null || peerStats.latestHandshakeEpochMillis == 0L) {
127+
peer.latestHandshakeLabel.visibility = View.GONE
128+
peer.latestHandshakeText.visibility = View.GONE
129+
} else {
130+
peer.latestHandshakeText.text = QuantityFormatter.formatEpochAgo(peerStats.latestHandshakeEpochMillis)
131+
peer.latestHandshakeLabel.visibility = View.VISIBLE
132+
peer.latestHandshakeText.visibility = View.VISIBLE
121133
}
122-
peer.transferText.text = getString(R.string.transfer_rx_tx, QuantityFormatter.formatBytes(rx), QuantityFormatter.formatBytes(tx))
123-
peer.transferLabel.visibility = View.VISIBLE
124-
peer.transferText.visibility = View.VISIBLE
125134
}
126135
} catch (e: Throwable) {
127136
for (i in 0 until binding.peersLayout.childCount) {
128137
val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i))
129138
?: continue
130139
peer.transferLabel.visibility = View.GONE
131140
peer.transferText.visibility = View.GONE
141+
peer.latestHandshakeLabel.visibility = View.GONE
142+
peer.latestHandshakeText.visibility = View.GONE
132143
}
133144
}
134145
}

ui/src/main/java/com/wireguard/android/util/QuantityFormatter.kt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,17 @@
55

66
package com.wireguard.android.util
77

8+
import android.icu.text.ListFormatter
9+
import android.icu.text.MeasureFormat
10+
import android.icu.text.RelativeDateTimeFormatter
11+
import android.icu.util.Measure
12+
import android.icu.util.MeasureUnit
13+
import android.os.Build
814
import com.wireguard.android.Application
915
import com.wireguard.android.R
16+
import java.util.Locale
17+
import kotlin.time.DurationUnit
18+
import kotlin.time.toDuration
1019

1120
object QuantityFormatter {
1221
fun formatBytes(bytes: Long): String {
@@ -19,4 +28,40 @@ object QuantityFormatter {
1928
else -> context.getString(R.string.transfer_tibibytes, bytes / (1024.0 * 1024.0 * 1024.0) / 1024.0)
2029
}
2130
}
31+
32+
fun formatEpochAgo(epochMillis: Long): String {
33+
var span = (System.currentTimeMillis() - epochMillis) / 1000
34+
35+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
36+
return span.toDuration(DurationUnit.SECONDS).toString()
37+
38+
if (span <= 0L)
39+
return RelativeDateTimeFormatter.getInstance().format(RelativeDateTimeFormatter.Direction.PLAIN, RelativeDateTimeFormatter.AbsoluteUnit.NOW)
40+
val measureFormat = MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
41+
val parts = ArrayList<CharSequence>(4)
42+
if (span >= 24 * 60 * 60L) {
43+
val v = span / (24 * 60 * 60L)
44+
parts.add(measureFormat.format(Measure(v, MeasureUnit.DAY)))
45+
span -= v * (24 * 60 * 60L)
46+
}
47+
if (span >= 60 * 60L) {
48+
val v = span / (60 * 60L)
49+
parts.add(measureFormat.format(Measure(v, MeasureUnit.HOUR)))
50+
span -= v * (60 * 60L)
51+
}
52+
if (span >= 60L) {
53+
val v = span / 60L
54+
parts.add(measureFormat.format(Measure(v, MeasureUnit.MINUTE)))
55+
span -= v * 60L
56+
}
57+
if (span > 0L)
58+
parts.add(measureFormat.format(Measure(span, MeasureUnit.SECOND)))
59+
60+
val joined = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
61+
parts.joinToString()
62+
else
63+
ListFormatter.getInstance(Locale.getDefault(), ListFormatter.Type.UNITS, ListFormatter.Width.SHORT).format(parts)
64+
65+
return Application.get().applicationContext.getString(R.string.latest_handshake_ago, joined)
66+
}
2267
}

ui/src/main/res/layout/tunnel_detail_peer.xml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@
171171
android:id="@+id/transfer_label"
172172
android:layout_width="wrap_content"
173173
android:layout_height="wrap_content"
174-
android:layout_below="@+id/endpoint_text"
174+
android:layout_below="@+id/persistent_keepalive_text"
175175
android:layout_marginTop="8dp"
176176
android:labelFor="@+id/transfer_text"
177177
android:text="@string/transfer"
@@ -194,6 +194,34 @@
194194
app:layout_constraintTop_toBottomOf="@+id/transfer_label"
195195
tools:text="1024 MB"
196196
tools:visibility="visible" />
197+
198+
<TextView
199+
android:id="@+id/latest_handshake_label"
200+
android:layout_width="wrap_content"
201+
android:layout_height="wrap_content"
202+
android:layout_below="@+id/transfer_text"
203+
android:layout_marginTop="8dp"
204+
android:labelFor="@+id/latest_handshake_text"
205+
android:text="@string/latest_handshake"
206+
android:visibility="gone"
207+
app:layout_constraintStart_toStartOf="parent"
208+
app:layout_constraintTop_toBottomOf="@+id/transfer_text"
209+
tools:visibility="visible" />
210+
211+
<TextView
212+
android:id="@+id/latest_handshake_text"
213+
android:layout_width="wrap_content"
214+
android:layout_height="wrap_content"
215+
android:layout_below="@+id/latest_handshake_label"
216+
android:contentDescription="@string/latest_handshake"
217+
android:nextFocusUp="@id/transfer_text"
218+
android:onClick="@{ClipboardUtils::copyTextView}"
219+
android:textAppearance="?attr/textAppearanceBodyLarge"
220+
android:visibility="gone"
221+
app:layout_constraintStart_toStartOf="parent"
222+
app:layout_constraintTop_toBottomOf="@+id/latest_handshake_label"
223+
tools:text="4 minutes, 27 seconds ago"
224+
tools:visibility="visible" />
197225
</androidx.constraintlayout.widget.ConstraintLayout>
198226
</com.google.android.material.card.MaterialCardView>
199227
</layout>

ui/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@
140140
<string name="key_length_explanation_base64">: WireGuard base64 keys must be 44 characters (32 bytes)</string>
141141
<string name="key_length_explanation_binary">: WireGuard keys must be 32 bytes</string>
142142
<string name="key_length_explanation_hex">: WireGuard hex keys must be 64 characters (32 bytes)</string>
143+
<string name="latest_handshake">Latest handshake</string>
144+
<string name="latest_handshake_ago">%s ago</string>
143145
<string name="listen_port">Listen port</string>
144146
<string name="log_export_error">Unable to export log: %s</string>
145147
<string name="log_export_subject">WireGuard Android Log File</string>

0 commit comments

Comments
 (0)