ui: display latest handshake time

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2023-04-24 18:07:03 +02:00
parent 20480992c4
commit b1b08ce716
4 changed files with 94 additions and 8 deletions

View File

@ -112,16 +112,25 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider {
val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i)) val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i))
?: continue ?: continue
val publicKey = peer.item!!.publicKey val publicKey = peer.item!!.publicKey
val rx = statistics.peerRx(publicKey) val peerStats = statistics.peer(publicKey)
val tx = statistics.peerTx(publicKey) if (peerStats == null || (peerStats.rxBytes == 0L && peerStats.txBytes == 0L)) {
if (rx == 0L && tx == 0L) {
peer.transferLabel.visibility = View.GONE peer.transferLabel.visibility = View.GONE
peer.transferText.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) { } catch (e: Throwable) {
for (i in 0 until binding.peersLayout.childCount) { for (i in 0 until binding.peersLayout.childCount) {
@ -129,6 +138,8 @@ class TunnelDetailFragment : BaseFragment(), MenuProvider {
?: continue ?: continue
peer.transferLabel.visibility = View.GONE peer.transferLabel.visibility = View.GONE
peer.transferText.visibility = View.GONE peer.transferText.visibility = View.GONE
peer.latestHandshakeLabel.visibility = View.GONE
peer.latestHandshakeText.visibility = View.GONE
} }
} }
} }

View File

@ -5,8 +5,17 @@
package com.wireguard.android.util 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.Application
import com.wireguard.android.R import com.wireguard.android.R
import java.util.Locale
import kotlin.time.DurationUnit
import kotlin.time.toDuration
object QuantityFormatter { object QuantityFormatter {
fun formatBytes(bytes: Long): String { 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) 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)
}
} }

View File

@ -171,7 +171,7 @@
android:id="@+id/transfer_label" android:id="@+id/transfer_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="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:layout_marginTop="8dp"
android:labelFor="@+id/transfer_text" android:labelFor="@+id/transfer_text"
android:text="@string/transfer" android:text="@string/transfer"
@ -194,6 +194,34 @@
app:layout_constraintTop_toBottomOf="@+id/transfer_label" app:layout_constraintTop_toBottomOf="@+id/transfer_label"
tools:text="1024 MB" tools:text="1024 MB"
tools:visibility="visible" /> 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> </androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
</layout> </layout>

View File

@ -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_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_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="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="listen_port">Listen port</string>
<string name="log_export_error">Unable to export log: %s</string> <string name="log_export_error">Unable to export log: %s</string>
<string name="log_export_subject">WireGuard Android Log File</string> <string name="log_export_subject">WireGuard Android Log File</string>