Add client count badge
This commit is contained in:
45
mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt
Normal file
45
mobile/src/main/java/be/mygod/vpnhotspot/client/Client.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
package be.mygod.vpnhotspot.client
|
||||
|
||||
import android.support.v7.util.DiffUtil
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.R
|
||||
import be.mygod.vpnhotspot.net.IpNeighbour
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
import java.util.*
|
||||
|
||||
abstract class Client {
|
||||
companion object : DiffUtil.ItemCallback<Client>() {
|
||||
override fun areItemsTheSame(oldItem: Client, newItem: Client) =
|
||||
oldItem.iface == newItem.iface && oldItem.mac == newItem.mac
|
||||
override fun areContentsTheSame(oldItem: Client, newItem: Client) = oldItem == newItem
|
||||
}
|
||||
|
||||
abstract val iface: String
|
||||
abstract val mac: String
|
||||
val ip = TreeMap<String, IpNeighbour.State>()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as Client
|
||||
|
||||
if (iface != other.iface) return false
|
||||
if (mac != other.mac) return false
|
||||
if (ip != other.ip) return false
|
||||
|
||||
return true
|
||||
}
|
||||
override fun hashCode() = Objects.hash(iface, mac, ip)
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package be.mygod.vpnhotspot.client
|
||||
|
||||
import android.app.Service
|
||||
import android.content.*
|
||||
import android.net.wifi.p2p.WifiP2pDevice
|
||||
import android.os.IBinder
|
||||
import be.mygod.vpnhotspot.RepeaterService
|
||||
import be.mygod.vpnhotspot.net.IpNeighbour
|
||||
import be.mygod.vpnhotspot.net.IpNeighbourMonitor
|
||||
import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.util.StickyEvent1
|
||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
||||
|
||||
class ClientMonitorService : Service(), ServiceConnection, IpNeighbourMonitor.Callback {
|
||||
inner class Binder : android.os.Binder() {
|
||||
val clientsChanged = StickyEvent1 { clients }
|
||||
}
|
||||
private val binder = Binder()
|
||||
override fun onBind(intent: Intent?) = binder
|
||||
|
||||
private var tetheredInterfaces = emptySet<String>()
|
||||
private val receiver = broadcastReceiver { _, intent ->
|
||||
tetheredInterfaces = TetheringManager.getTetheredIfaces(intent.extras).toSet() +
|
||||
TetheringManager.getLocalOnlyTetheredIfaces(intent.extras)
|
||||
populateClients()
|
||||
}
|
||||
|
||||
private var repeater: RepeaterService.Binder? = null
|
||||
private var p2p: Collection<WifiP2pDevice> = emptyList()
|
||||
private var neighbours = emptyList<IpNeighbour>()
|
||||
private var clients = emptyList<Client>()
|
||||
private set(value) {
|
||||
field = value
|
||||
binder.clientsChanged(value)
|
||||
}
|
||||
|
||||
private fun populateClients() {
|
||||
val clients = HashMap<Pair<String, String>, Client>()
|
||||
val group = repeater?.service?.group
|
||||
val p2pInterface = group?.`interface`
|
||||
if (p2pInterface != null) {
|
||||
for (client in p2p) clients[Pair(p2pInterface, client.deviceAddress)] = WifiP2pClient(p2pInterface, client)
|
||||
}
|
||||
for (neighbour in neighbours) {
|
||||
val key = Pair(neighbour.dev, neighbour.lladdr)
|
||||
var client = clients[key]
|
||||
if (client == null) {
|
||||
if (!tetheredInterfaces.contains(neighbour.dev)) continue
|
||||
client = TetheringClient(neighbour)
|
||||
clients[key] = client
|
||||
}
|
||||
client.ip += Pair(neighbour.ip, neighbour.state)
|
||||
}
|
||||
this.clients = clients.values.sortedWith(compareBy<Client> { it.iface }.thenBy { it.mac })
|
||||
}
|
||||
|
||||
private fun refreshP2p() {
|
||||
val repeater = repeater
|
||||
p2p = (if (repeater?.active != true) null else repeater.service.group?.clientList) ?: emptyList()
|
||||
populateClients()
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
bindService(Intent(this, RepeaterService::class.java), this, Context.BIND_AUTO_CREATE)
|
||||
IpNeighbourMonitor.registerCallback(this)
|
||||
registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
unregisterReceiver(receiver)
|
||||
IpNeighbourMonitor.unregisterCallback(this)
|
||||
unbindService(this)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
val binder = service as RepeaterService.Binder
|
||||
repeater = binder
|
||||
binder.statusChanged[this] = this::refreshP2p
|
||||
binder.groupChanged[this] = { refreshP2p() }
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
val binder = repeater ?: return
|
||||
repeater = null
|
||||
binder.statusChanged -= this
|
||||
binder.groupChanged -= this
|
||||
}
|
||||
|
||||
override fun onIpNeighbourAvailable(neighbours: List<IpNeighbour>) {
|
||||
this.neighbours = neighbours
|
||||
populateClients()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package be.mygod.vpnhotspot.client
|
||||
|
||||
import be.mygod.vpnhotspot.net.IpNeighbour
|
||||
|
||||
class TetheringClient(private val neighbour: IpNeighbour) : Client() {
|
||||
override val iface get() = neighbour.dev
|
||||
override val mac get() = neighbour.lladdr
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package be.mygod.vpnhotspot.client
|
||||
|
||||
import android.net.wifi.p2p.WifiP2pDevice
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
|
||||
class WifiP2pClient(p2pInterface: String, p2p: WifiP2pDevice) : Client() {
|
||||
override val iface = p2pInterface
|
||||
override val mac = p2p.deviceAddress ?: ""
|
||||
override val icon: Int get() = TetherType.WIFI_P2P.icon
|
||||
}
|
||||
Reference in New Issue
Block a user