VPN Hotspot 2.0: Client+ (#39)

Fix #13, #38. I don't have a lot of confidence that this would work very well for every device.

Also here's an SQL command that hopefully somebody could make into the app for me: `SELECT TrafficRecord.mac, SUM(TrafficRecord.sentPackets), SUM(TrafficRecord.sentBytes), SUM(TrafficRecord.receivedPackets), SUM(TrafficRecord.receivedBytes) FROM TrafficRecord LEFT JOIN TrafficRecord AS Next ON TrafficRecord.id = Next.previousId WHERE Next.id IS NULL GROUP BY TrafficRecord.mac;`
This commit is contained in:
Mygod
2018-10-02 21:12:19 +08:00
committed by GitHub
parent 16d1eda0d4
commit 38f95a382e
35 changed files with 946 additions and 98 deletions

View File

@@ -1,13 +1,26 @@
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
import be.mygod.vpnhotspot.net.InetAddressComparator
import be.mygod.vpnhotspot.net.IpNeighbour
import be.mygod.vpnhotspot.net.TetherType
import java.util.*
import be.mygod.vpnhotspot.room.AppDatabase
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
abstract class Client {
abstract class Client : BaseObservable() {
companion object DiffCallback : DiffUtil.ItemCallback<Client>() {
override fun areItemsTheSame(oldItem: Client, newItem: Client) =
oldItem.iface == newItem.iface && oldItem.mac == newItem.mac
@@ -16,17 +29,31 @@ abstract class Client {
abstract val iface: String
abstract val mac: String
val ip = TreeMap<String, IpNeighbour.State>()
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 get() = "$mac%$iface"
val description get() = ip.entries.joinToString("\n") { (ip, state) ->
app.getString(when (state) {
IpNeighbour.State.INCOMPLETE -> R.string.connected_state_incomplete
IpNeighbour.State.VALID -> R.string.connected_state_valid
IpNeighbour.State.FAILED -> R.string.connected_state_failed
else -> throw IllegalStateException("Invalid IpNeighbour.State: $state")
}, ip)
val title: CharSequence get() {
val result = SpannableStringBuilder(record.nickname.onEmpty(macIface))
if (record.blocked) result.setSpan(StrikethroughSpan(), 0, result.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
return result
}
val description: String @Bindable get() {
val result = StringBuilder(if (record.nickname.isEmpty()) "" else "$macIface\n")
ip.entries.forEach { (ip, state) ->
result.appendln(app.getString(when (state) {
IpNeighbour.State.INCOMPLETE -> R.string.connected_state_incomplete
IpNeighbour.State.VALID -> R.string.connected_state_valid
IpNeighbour.State.FAILED -> R.string.connected_state_failed
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()
}
override fun equals(other: Any?): Boolean {
@@ -38,8 +65,9 @@ abstract class Client {
if (iface != other.iface) return false
if (mac != other.mac) return false
if (ip != other.ip) return false
if (record != other.record) return false
return true
}
override fun hashCode() = Objects.hash(iface, mac, ip)
override fun hashCode() = Objects.hash(iface, mac, ip, record)
}