From 48d6307b2b1e2e9b3e84b37ca8319d0ef13afef7 Mon Sep 17 00:00:00 2001 From: Mygod Date: Wed, 3 Oct 2018 15:58:49 +0800 Subject: [PATCH] Fix rate not working for polling clients --- .../java/be/mygod/vpnhotspot/client/Client.kt | 14 ++---- .../vpnhotspot/client/ClientsFragment.kt | 43 ++++++++++++------- .../src/main/res/layout/listitem_client.xml | 11 ++++- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt index aa75ddf4..56ddf56f 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt @@ -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() { 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(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() } diff --git a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt index de6b6800..d8632025 100644 --- a/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt +++ b/mobile/src/main/java/be/mygod/vpnhotspot/client/ClientsFragment.kt @@ -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) { - private var clientsLookup: LongSparseArray? = null override fun submitList(list: MutableList?) { super.submitList(list) - clientsLookup = if (list == null) null else LongSparseArray(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, oldRecords: LongSparseArray) { - val clientsLookup = clientsLookup ?: return - val seenClients = HashSet() + 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, TrafficRate>() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { binding = DataBindingUtil.inflate(inflater, R.layout.fragment_clients, container, false) diff --git a/mobile/src/main/res/layout/listitem_client.xml b/mobile/src/main/res/layout/listitem_client.xml index fbddf4eb..e394320d 100644 --- a/mobile/src/main/res/layout/listitem_client.xml +++ b/mobile/src/main/res/layout/listitem_client.xml @@ -6,6 +6,9 @@ + - + +