Fix rate not working for polling clients

This commit is contained in:
Mygod
2018-10-03 15:58:49 +08:00
parent c0ee5a0b89
commit 48d6307b2b
3 changed files with 40 additions and 28 deletions

View File

@@ -2,10 +2,7 @@ package be.mygod.vpnhotspot.client
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.format.Formatter
import android.text.style.StrikethroughSpan
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import androidx.recyclerview.widget.DiffUtil
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R
@@ -17,10 +14,9 @@ import be.mygod.vpnhotspot.room.lookup
import be.mygod.vpnhotspot.room.macToLong
import be.mygod.vpnhotspot.util.onEmpty
import java.net.InetAddress
import java.util.Objects
import java.util.TreeMap
import java.util.*
abstract class Client : BaseObservable() {
abstract class Client {
companion object DiffCallback : DiffUtil.ItemCallback<Client>() {
override fun areItemsTheSame(oldItem: Client, newItem: Client) =
oldItem.iface == newItem.iface && oldItem.mac == newItem.mac
@@ -32,8 +28,6 @@ abstract class Client : BaseObservable() {
private val macIface get() = "$mac%$iface"
val ip = TreeMap<InetAddress, IpNeighbour.State>(InetAddressComparator)
val record by lazy { AppDatabase.instance.clientRecordDao.lookup(mac.macToLong()) }
var sendRate = -1L
var receiveRate = -1L
open val icon get() = TetherType.ofInterface(iface).icon
val title: CharSequence get() {
@@ -41,7 +35,7 @@ abstract class Client : BaseObservable() {
if (record.blocked) result.setSpan(StrikethroughSpan(), 0, result.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
return result
}
val description: String @Bindable get() {
val description: String get() {
val result = StringBuilder(if (record.nickname.isEmpty()) "" else "$macIface\n")
ip.entries.forEach { (ip, state) ->
result.appendln(app.getString(when (state) {
@@ -51,8 +45,6 @@ abstract class Client : BaseObservable() {
else -> throw IllegalStateException("Invalid IpNeighbour.State: $state")
}, ip.hostAddress))
}
if (sendRate >= 0 && receiveRate >= 0) result.appendln(
"${Formatter.formatFileSize(app, sendRate)}/s\t\t${Formatter.formatFileSize(app, receiveRate)}/s")
return result.toString().trimEnd()
}

View File

@@ -16,6 +16,7 @@ import android.widget.EditText
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.PopupMenu
import androidx.core.os.bundleOf
import androidx.databinding.BaseObservable
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator
@@ -23,7 +24,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import be.mygod.vpnhotspot.AlertDialogFragment
import be.mygod.vpnhotspot.BR
import be.mygod.vpnhotspot.App.Companion.app
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.databinding.FragmentClientsBinding
import be.mygod.vpnhotspot.databinding.ListitemClientBinding
@@ -31,6 +32,7 @@ import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
import be.mygod.vpnhotspot.net.monitor.TrafficRecorder
import be.mygod.vpnhotspot.room.*
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
import be.mygod.vpnhotspot.util.computeIfAbsentCompat
import be.mygod.vpnhotspot.util.toPluralInt
import be.mygod.vpnhotspot.widget.SmartSnackbar
import java.text.NumberFormat
@@ -92,6 +94,16 @@ class ClientsFragment : Fragment(), ServiceConnection {
}
}
data class TrafficRate(var send: Long = -1, var receive: Long = -1) : BaseObservable() {
fun clear() {
send = -1
receive = -1
}
override fun toString() = if (send < 0 || receive < 0) "" else
"${Formatter.formatFileSize(app, send)}/s\t\t${Formatter.formatFileSize(app, receive)}/s"
}
private inner class ClientViewHolder(val binding: ListitemClientBinding) : RecyclerView.ViewHolder(binding.root),
View.OnClickListener, PopupMenu.OnMenuItemClickListener {
init {
@@ -144,12 +156,8 @@ class ClientsFragment : Fragment(), ServiceConnection {
}
private inner class ClientAdapter : ListAdapter<Client, ClientViewHolder>(Client) {
private var clientsLookup: LongSparseArray<Client>? = null
override fun submitList(list: MutableList<Client>?) {
super.submitList(list)
clientsLookup = if (list == null) null else LongSparseArray<Client>(list.size).apply {
list.forEach { put(it.mac.macToLong(), it) }
}
binding.swipeRefresher.isRefreshing = false
}
@@ -157,20 +165,17 @@ class ClientsFragment : Fragment(), ServiceConnection {
ClientViewHolder(ListitemClientBinding.inflate(LayoutInflater.from(parent.context), parent, false))
override fun onBindViewHolder(holder: ClientViewHolder, position: Int) {
holder.binding.client = getItem(position)
val client = getItem(position)
holder.binding.client = client
holder.binding.rate =
rates.computeIfAbsentCompat(Pair(client.iface, client.mac.macToLong())) { TrafficRate() }
holder.binding.executePendingBindings()
}
fun updateTraffic(newRecords: Collection<TrafficRecord>, oldRecords: LongSparseArray<TrafficRecord>) {
val clientsLookup = clientsLookup ?: return
val seenClients = HashSet<Client>()
for (rate in rates.values) rate.clear()
for (newRecord in newRecords) {
val oldRecord = oldRecords[newRecord.previousId ?: continue] ?: continue
val client = clientsLookup[newRecord.mac]
if (seenClients.add(client)) {
client.sendRate = 0
client.receiveRate = 0
}
val elapsed = newRecord.timestamp - oldRecord.timestamp
if (elapsed == 0L) {
check(newRecord.sentPackets == oldRecord.sentPackets)
@@ -178,17 +183,23 @@ class ClientsFragment : Fragment(), ServiceConnection {
check(newRecord.receivedPackets == oldRecord.receivedPackets)
check(newRecord.receivedBytes == oldRecord.receivedBytes)
} else {
client.sendRate += (newRecord.sentBytes - oldRecord.sentBytes) * 1000 / elapsed
client.receiveRate += (newRecord.receivedBytes - oldRecord.receivedBytes) * 1000 / elapsed
client.notifyPropertyChanged(BR.description)
val rate = rates.computeIfAbsentCompat(Pair(newRecord.downstream, newRecord.mac)) { TrafficRate() }
if (rate.send < 0 || rate.receive < 0) {
rate.send = 0
rate.receive = 0
}
rate.send += (newRecord.sentBytes - oldRecord.sentBytes) * 1000 / elapsed
rate.receive += (newRecord.receivedBytes - oldRecord.receivedBytes) * 1000 / elapsed
}
}
for (rate in rates.values) rate.notifyChange()
}
}
private lateinit var binding: FragmentClientsBinding
private val adapter = ClientAdapter()
private var clients: ClientMonitorService.Binder? = null
private var rates = HashMap<Pair<String, Long>, TrafficRate>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_clients, container, false)

View File

@@ -6,6 +6,9 @@
<variable
name="client"
type="be.mygod.vpnhotspot.client.Client"/>
<variable
name="rate"
type="be.mygod.vpnhotspot.client.ClientsFragment.TrafficRate"/>
</data>
<LinearLayout
@@ -41,12 +44,18 @@
android:textIsSelectable="@{client.record.nickname.length() == 0}"
tools:text="01:23:45:ab:cd:ef%p2p-p2p0-0"/>
<TextView
<be.mygod.vpnhotspot.widget.AutoCollapseTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{client.description}"
android:textIsSelectable="true"
tools:text="192.168.49.123 (reachable)\nfe80::abcd:efff:1234:5678%p2p-p2p0-0 (reachable)"/>
<be.mygod.vpnhotspot.widget.AutoCollapseTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{rate.toString()}"
tools:text="▲ 3.23KB/s\t\t▼ 5.12GB/s"/>
</LinearLayout>
</LinearLayout>
</layout>