1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
/*
* Copyright © 2017-2021 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 androidx.lifecycle.lifecycleScope
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.util.QuantityFormatter
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
/**
* Fragment that shows details about a specific tunnel.
*/
class TunnelDetailFragment : BaseFragment() {
private var binding: TunnelDetailFragmentBinding? = null
private var lastState = Tunnel.State.TOGGLE
private var timerActive = true
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?.executePendingBindings()
return binding?.root
}
override fun onDestroyView() {
binding = null
super.onDestroyView()
}
override fun onResume() {
super.onResume()
timerActive = true
lifecycleScope.launch {
while (timerActive) {
updateStats()
delay(1000)
}
}
}
override fun onSelectedTunnelChanged(oldTunnel: ObservableTunnel?, newTunnel: ObservableTunnel?) {
val binding = binding ?: return
binding.tunnel = newTunnel
if (newTunnel == null) {
binding.config = null
} else {
lifecycleScope.launch {
try {
binding.config = newTunnel.getConfigAsync()
} catch (_: Throwable) {
binding.config = null
}
}
}
lastState = Tunnel.State.TOGGLE
lifecycleScope.launch { updateStats() }
}
override fun onStop() {
timerActive = false
super.onStop()
}
override fun onViewStateRestored(savedInstanceState: Bundle?) {
binding ?: return
binding!!.fragment = this
onSelectedTunnelChanged(null, selectedTunnel)
super.onViewStateRestored(savedInstanceState)
}
private suspend fun updateStats() {
val binding = binding ?: return
val tunnel = binding.tunnel ?: return
if (!isResumed) return
val state = tunnel.state
if (state != Tunnel.State.UP && lastState == state) return
lastState = state
try {
val statistics = tunnel.getStatisticsAsync()
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 = 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) {
val peer: TunnelDetailPeerBinding = DataBindingUtil.getBinding(binding.peersLayout.getChildAt(i))
?: continue
peer.transferLabel.visibility = View.GONE
peer.transferText.visibility = View.GONE
}
}
}
}
|