Add client count badge
This commit is contained in:
@@ -24,6 +24,7 @@ allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ dependencies {
|
||||
implementation "com.android.support:design:$supportLibraryVersion"
|
||||
implementation "com.android.support:preference-v14:$supportLibraryVersion"
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
|
||||
implementation 'com.github.luongvo:BadgeView:1.1.5'
|
||||
implementation 'com.linkedin.dexmaker:dexmaker-mockito:2.16.0'
|
||||
implementation "com.takisoft.fix:preference-v7:$takisoftFixVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".client.ClientMonitorService"/>
|
||||
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
|
||||
@@ -9,10 +9,11 @@ abstract class IpNeighbourMonitoringService : Service(), IpNeighbourMonitor.Call
|
||||
|
||||
protected abstract val activeIfaces: List<String>
|
||||
|
||||
override fun onIpNeighbourAvailable(neighbours: Map<String, IpNeighbour>) {
|
||||
this.neighbours = neighbours.values.toList()
|
||||
override fun onIpNeighbourAvailable(neighbours: List<IpNeighbour>) {
|
||||
this.neighbours = neighbours
|
||||
updateNotification()
|
||||
}
|
||||
override fun postIpNeighbourAvailable() {
|
||||
protected fun updateNotification() {
|
||||
val sizeLookup = neighbours.groupBy { it.dev }.mapValues { (_, neighbours) ->
|
||||
neighbours
|
||||
.filter { it.state != IpNeighbour.State.FAILED }
|
||||
|
||||
@@ -1,21 +1,39 @@
|
||||
package be.mygod.vpnhotspot
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.ServiceConnection
|
||||
import android.databinding.DataBindingUtil
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.support.design.internal.BottomNavigationMenuView
|
||||
import android.support.design.widget.BottomNavigationView
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.Gravity
|
||||
import android.view.MenuItem
|
||||
import be.mygod.vpnhotspot.client.ClientMonitorService
|
||||
import be.mygod.vpnhotspot.databinding.ActivityMainBinding
|
||||
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
|
||||
import q.rorbin.badgeview.QBadgeView
|
||||
|
||||
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
|
||||
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener, ServiceConnection {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var badge: QBadgeView
|
||||
private var clients: ClientMonitorService.Binder? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
|
||||
binding.navigation.setOnNavigationItemSelectedListener(this)
|
||||
if (savedInstanceState == null) displayFragment(RepeaterFragment())
|
||||
badge = QBadgeView(this)
|
||||
badge.bindTarget((binding.navigation.getChildAt(0) as BottomNavigationMenuView).getChildAt(0))
|
||||
badge.badgeBackgroundColor = ContextCompat.getColor(this, R.color.colorAccent)
|
||||
badge.badgeTextColor = ContextCompat.getColor(this, R.color.primary_text_default_material_light)
|
||||
badge.badgeGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
|
||||
badge.setGravityOffset(16f, 0f, true)
|
||||
ServiceForegroundConnector(this, ClientMonitorService::class)
|
||||
}
|
||||
|
||||
override fun onNavigationItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
@@ -43,6 +61,17 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
clients = service as ClientMonitorService.Binder
|
||||
service.clientsChanged[this] = { badge.badgeNumber = it.size }
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
val clients = clients ?: return
|
||||
this.clients = null
|
||||
clients.clientsChanged -= this
|
||||
}
|
||||
|
||||
private fun displayFragment(fragment: Fragment) =
|
||||
supportFragmentManager.beginTransaction().replace(R.id.fragmentHolder, fragment).commitAllowingStateLoss()
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package be.mygod.vpnhotspot
|
||||
|
||||
import android.content.*
|
||||
import android.content.ComponentName
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.databinding.BaseObservable
|
||||
import android.databinding.Bindable
|
||||
import android.databinding.DataBindingUtil
|
||||
import android.net.wifi.WifiConfiguration
|
||||
import android.net.wifi.p2p.WifiP2pDevice
|
||||
import android.net.wifi.p2p.WifiP2pGroup
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
@@ -14,7 +16,6 @@ import android.support.v4.content.ContextCompat
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.AppCompatDialog
|
||||
import android.support.v7.recyclerview.extensions.ListAdapter
|
||||
import android.support.v7.util.DiffUtil
|
||||
import android.support.v7.widget.DefaultItemAnimator
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
@@ -23,22 +24,19 @@ import android.view.*
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import be.mygod.vpnhotspot.App.Companion.app
|
||||
import be.mygod.vpnhotspot.client.Client
|
||||
import be.mygod.vpnhotspot.client.ClientMonitorService
|
||||
import be.mygod.vpnhotspot.databinding.FragmentRepeaterBinding
|
||||
import be.mygod.vpnhotspot.databinding.ListitemClientBinding
|
||||
import be.mygod.vpnhotspot.net.IpNeighbour
|
||||
import be.mygod.vpnhotspot.net.IpNeighbourMonitor
|
||||
import be.mygod.vpnhotspot.net.TetherType
|
||||
import be.mygod.vpnhotspot.net.TetheringManager
|
||||
import be.mygod.vpnhotspot.net.wifi.P2pSupplicantConfiguration
|
||||
import be.mygod.vpnhotspot.net.wifi.WifiP2pDialog
|
||||
import be.mygod.vpnhotspot.util.ServiceForegroundConnector
|
||||
import be.mygod.vpnhotspot.util.broadcastReceiver
|
||||
import be.mygod.vpnhotspot.util.formatAddresses
|
||||
import java.net.NetworkInterface
|
||||
import java.net.SocketException
|
||||
import java.util.*
|
||||
|
||||
class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener, IpNeighbourMonitor.Callback {
|
||||
class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickListener {
|
||||
inner class Data : BaseObservable() {
|
||||
val switchEnabled: Boolean
|
||||
@Bindable get() = when (binder?.service?.status) {
|
||||
@@ -76,70 +74,19 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
||||
fun onStatusChanged() {
|
||||
notifyPropertyChanged(BR.switchEnabled)
|
||||
notifyPropertyChanged(BR.serviceStarted)
|
||||
if (binder?.active != true) onGroupChanged()
|
||||
notifyPropertyChanged(BR.addresses)
|
||||
}
|
||||
fun onGroupChanged(group: WifiP2pGroup? = null) {
|
||||
notifyPropertyChanged(BR.ssid)
|
||||
p2pInterface = group?.`interface`
|
||||
notifyPropertyChanged(BR.addresses)
|
||||
adapter.p2p = group?.clientList ?: emptyList()
|
||||
adapter.recreate()
|
||||
}
|
||||
}
|
||||
|
||||
inner class Client(p2p: WifiP2pDevice? = null, neighbour: IpNeighbour? = null) {
|
||||
val iface = neighbour?.dev ?: p2pInterface
|
||||
val mac = p2p?.deviceAddress ?: neighbour?.lladdr!!
|
||||
val ip = TreeMap<String, IpNeighbour.State>()
|
||||
|
||||
val icon get() = TetherType.ofInterface(iface, p2pInterface).icon
|
||||
val title get() = "$mac%$iface"
|
||||
val description get() = ip.entries.joinToString("\n") { (ip, state) ->
|
||||
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)
|
||||
}
|
||||
private object ClientDiffCallback : 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
|
||||
}
|
||||
private class ClientViewHolder(val binding: ListitemClientBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
private inner class ClientAdapter : ListAdapter<Client, ClientViewHolder>(ClientDiffCallback) {
|
||||
var p2p: Collection<WifiP2pDevice> = emptyList()
|
||||
var neighbours = emptyList<IpNeighbour>()
|
||||
|
||||
fun recreate() {
|
||||
val p2p = HashMap(p2p.associateBy({ Pair(p2pInterface, it.deviceAddress) }, { Client(it) }))
|
||||
for (neighbour in neighbours) {
|
||||
val key = Pair(neighbour.dev, neighbour.lladdr)
|
||||
var client = p2p[key]
|
||||
if (client == null) {
|
||||
if (!tetheredInterfaces.contains(neighbour.dev)) continue
|
||||
client = Client(neighbour = neighbour)
|
||||
p2p[key] = client
|
||||
}
|
||||
client.ip += Pair(neighbour.ip, neighbour.state)
|
||||
}
|
||||
submitList(p2p.values.sortedWith(compareBy<Client> { it.iface }.thenBy { it.mac }))
|
||||
private inner class ClientAdapter : ListAdapter<Client, ClientViewHolder>(Client) {
|
||||
override fun submitList(list: MutableList<Client>?) {
|
||||
super.submitList(list)
|
||||
binding.swipeRefresher.isRefreshing = false
|
||||
}
|
||||
|
||||
@@ -157,12 +104,7 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
||||
private val adapter = ClientAdapter()
|
||||
private var binder: RepeaterService.Binder? = null
|
||||
private var p2pInterface: String? = null
|
||||
private var tetheredInterfaces = emptySet<String>()
|
||||
private val receiver = broadcastReceiver { _, intent ->
|
||||
tetheredInterfaces = TetheringManager.getTetheredIfaces(intent.extras).toSet() +
|
||||
TetheringManager.getLocalOnlyTetheredIfaces(intent.extras)
|
||||
adapter.recreate()
|
||||
}
|
||||
private var clients: ClientMonitorService.Binder? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_repeater, container, false)
|
||||
@@ -175,28 +117,19 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
||||
IpNeighbourMonitor.instance?.flush()
|
||||
val binder = binder
|
||||
if (binder?.active == false) binder.requestGroupUpdate()
|
||||
adapter.recreate()
|
||||
}
|
||||
binding.toolbar.inflateMenu(R.menu.repeater)
|
||||
binding.toolbar.setOnMenuItemClickListener(this)
|
||||
ServiceForegroundConnector(this, RepeaterService::class)
|
||||
ServiceForegroundConnector(this, RepeaterService::class, ClientMonitorService::class)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
IpNeighbourMonitor.registerCallback(this)
|
||||
requireContext().registerReceiver(receiver, IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED))
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
requireContext().unregisterReceiver(receiver)
|
||||
IpNeighbourMonitor.unregisterCallback(this)
|
||||
onServiceDisconnected(null)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
if (service is ClientMonitorService.Binder) {
|
||||
clients = service
|
||||
service.clientsChanged[this] = { adapter.submitList(it.toMutableList()) }
|
||||
return
|
||||
}
|
||||
val binder = service as RepeaterService.Binder
|
||||
this.binder = binder
|
||||
binder.statusChanged[this] = data::onStatusChanged
|
||||
@@ -204,10 +137,16 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
if (name == ComponentName(requireContext(), ClientMonitorService::class.java)) {
|
||||
val clients = clients ?: return
|
||||
this.clients = null
|
||||
clients.clientsChanged -= this
|
||||
return
|
||||
}
|
||||
val binder = binder ?: return
|
||||
this.binder = null
|
||||
binder.statusChanged -= this
|
||||
binder.groupChanged -= this
|
||||
this.binder = null
|
||||
data.onStatusChanged()
|
||||
}
|
||||
|
||||
@@ -261,9 +200,4 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
|
||||
}
|
||||
Toast.makeText(context, R.string.repeater_configure_failure, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
override fun onIpNeighbourAvailable(neighbours: Map<String, IpNeighbour>) {
|
||||
adapter.neighbours = neighbours.values.toList()
|
||||
}
|
||||
override fun postIpNeighbourAvailable() = adapter.recreate()
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ class TetheringService : IpNeighbourMonitoringService(), VpnMonitor.Callback {
|
||||
VpnMonitor.registerCallback(this)
|
||||
receiverRegistered = true
|
||||
}
|
||||
postIpNeighbourAvailable()
|
||||
updateNotification()
|
||||
}
|
||||
if (routings.isEmpty()) {
|
||||
unregisterReceiver()
|
||||
|
||||
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
|
||||
}
|
||||
@@ -25,8 +25,7 @@ class IpNeighbourMonitor private constructor() : Runnable {
|
||||
instance = monitor
|
||||
monitor.flush()
|
||||
} else {
|
||||
synchronized(monitor.neighbours) { callback.onIpNeighbourAvailable(monitor.neighbours) }
|
||||
callback.postIpNeighbourAvailable()
|
||||
callback.onIpNeighbourAvailable(synchronized(monitor.neighbours) { monitor.neighbours.values.toList() })
|
||||
}
|
||||
}
|
||||
fun unregisterCallback(callback: Callback) {
|
||||
@@ -37,8 +36,7 @@ class IpNeighbourMonitor private constructor() : Runnable {
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun onIpNeighbourAvailable(neighbours: Map<String, IpNeighbour>)
|
||||
fun postIpNeighbourAvailable()
|
||||
fun onIpNeighbourAvailable(neighbours: List<IpNeighbour>)
|
||||
}
|
||||
|
||||
private val handler = Handler()
|
||||
@@ -112,11 +110,11 @@ class IpNeighbourMonitor private constructor() : Runnable {
|
||||
private fun postUpdateLocked() {
|
||||
if (updatePosted || instance != this) return
|
||||
handler.post {
|
||||
synchronized(neighbours) {
|
||||
for (callback in callbacks) callback.onIpNeighbourAvailable(neighbours)
|
||||
val neighbours = synchronized(neighbours) {
|
||||
updatePosted = false
|
||||
neighbours.values.toList()
|
||||
}
|
||||
for (callback in callbacks) callback.postIpNeighbourAvailable()
|
||||
for (callback in callbacks) callback.onIpNeighbourAvailable(neighbours)
|
||||
}
|
||||
updatePosted = true
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<data>
|
||||
<variable
|
||||
name="client"
|
||||
type="be.mygod.vpnhotspot.RepeaterFragment.Client"/>
|
||||
type="be.mygod.vpnhotspot.client.Client"/>
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
|
||||
Reference in New Issue
Block a user