summaryrefslogtreecommitdiffhomepage
path: root/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt
diff options
context:
space:
mode:
Diffstat (limited to 'ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt')
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt138
1 files changed, 138 insertions, 0 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
new file mode 100644
index 00000000..22e5d94e
--- /dev/null
+++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelDetailFragment.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright © 2017-2019 WireGuard LLC. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package com.wireguard.android.fragment
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import com.wireguard.android.R
+import com.wireguard.android.backend.Tunnel
+import com.wireguard.android.databinding.TunnelDetailFragmentBinding
+import com.wireguard.android.databinding.TunnelDetailPeerBinding
+import com.wireguard.android.model.ObservableTunnel
+import com.wireguard.android.ui.EdgeToEdge.setUpRoot
+import com.wireguard.android.ui.EdgeToEdge.setUpScrollingContent
+import com.wireguard.config.Config
+import java.util.Timer
+import java.util.TimerTask
+
+/**
+ * Fragment that shows details about a specific tunnel.
+ */
+class TunnelDetailFragment : BaseFragment() {
+ private var binding: TunnelDetailFragmentBinding? = null
+ private var lastState: Tunnel.State? = Tunnel.State.TOGGLE
+ private var timer: Timer? = null
+
+ private fun formatBytes(bytes: Long): String {
+ val context = requireContext()
+ return when {
+ bytes < 1024 -> context.getString(R.string.transfer_bytes, bytes)
+ bytes < 1024 * 1024 -> context.getString(R.string.transfer_kibibytes, bytes / 1024.0)
+ bytes < 1024 * 1024 * 1024 -> context.getString(R.string.transfer_mibibytes, bytes / (1024.0 * 1024.0))
+ bytes < 1024 * 1024 * 1024 * 1024L -> context.getString(R.string.transfer_gibibytes, bytes / (1024.0 * 1024.0 * 1024.0))
+ else -> context.getString(R.string.transfer_tibibytes, bytes / (1024.0 * 1024.0 * 1024.0) / 1024.0)
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ inflater.inflate(R.menu.tunnel_detail, menu)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ super.onCreateView(inflater, container, savedInstanceState)
+ binding = TunnelDetailFragmentBinding.inflate(inflater, container, false)
+ binding?.apply {
+ executePendingBindings()
+ setUpRoot(root as ViewGroup)
+ setUpScrollingContent(root as ViewGroup, null)
+ }
+ return binding!!.root
+ }
+
+ override fun onDestroyView() {
+ binding = null
+ super.onDestroyView()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ timer = Timer()
+ timer!!.scheduleAtFixedRate(object : TimerTask() {
+ override fun run() {
+ updateStats()
+ }
+ }, 0, 1000)
+ }
+
+ override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) {
+ if (binding == null) return
+ binding!!.tunnel = newTunnel
+ if (newTunnel == null) binding!!.config = null else newTunnel.configAsync.thenAccept { config: Config? -> binding!!.config = config }
+ lastState = Tunnel.State.TOGGLE
+ updateStats()
+ }
+
+ override fun onStop() {
+ super.onStop()
+ if (timer != null) {
+ timer!!.cancel()
+ timer = null
+ }
+ }
+
+ override fun onViewStateRestored(savedInstanceState: Bundle?) {
+ if (binding == null) {
+ return
+ }
+ binding!!.fragment = this
+ onSelectedTunnelChanged(null, selectedTunnel)
+ super.onViewStateRestored(savedInstanceState)
+ }
+
+ private fun updateStats() {
+ if (binding == null || !isResumed) return
+ val tunnel = binding!!.tunnel ?: return
+ val state = tunnel.state
+ if (state != Tunnel.State.UP && lastState == state) return
+ lastState = state
+ tunnel.statisticsAsync.whenComplete { statistics, throwable ->
+ if (throwable != null) {
+ for (i in 0 until binding!!.peersLayout.childCount) {
+ val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding!!.peersLayout.getChildAt(i))
+ ?: continue
+ peer.transferLabel.visibility = View.GONE
+ peer.transferText.visibility = View.GONE
+ }
+ return@whenComplete
+ }
+ for (i in 0 until binding!!.peersLayout.childCount) {
+ 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) {
+ peer.transferLabel.visibility = View.GONE
+ peer.transferText.visibility = View.GONE
+ continue
+ }
+ peer.transferText.text = requireContext().getString(R.string.transfer_rx_tx, formatBytes(rx), formatBytes(tx))
+ peer.transferLabel.visibility = View.VISIBLE
+ peer.transferText.visibility = View.VISIBLE
+ }
+ }
+ }
+}