Add device count to other tiles too

This commit is contained in:
Mygod
2020-06-16 10:22:57 +08:00
parent 6ae4207e32
commit 7b1f610f9a
4 changed files with 70 additions and 21 deletions

View File

@@ -0,0 +1,38 @@
package be.mygod.vpnhotspot.manage
import android.service.quicksettings.Tile
import androidx.annotation.RequiresApi
import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.net.IpNeighbour
import be.mygod.vpnhotspot.net.monitor.IpNeighbourMonitor
import be.mygod.vpnhotspot.util.KillableTileService
@RequiresApi(24)
abstract class IpNeighbourMonitoringTileService : KillableTileService(), IpNeighbourMonitor.Callback {
private var neighbours: Collection<IpNeighbour> = emptyList()
abstract fun updateTile()
override fun onStartListening() {
super.onStartListening()
IpNeighbourMonitor.registerCallback(this)
}
override fun onStopListening() {
IpNeighbourMonitor.unregisterCallback(this)
super.onStopListening()
}
protected fun Tile.subtitleDevices(filter: (String) -> Boolean) {
val size = neighbours
.filter { it.state != IpNeighbour.State.FAILED && filter(it.dev) }
.distinctBy { it.lladdr }
.size
if (size > 0) subtitle(resources.getQuantityString(
R.plurals.quick_settings_hotspot_secondary_label_num_devices, size, size))
}
override fun onIpNeighbourAvailable(neighbours: Collection<IpNeighbour>) {
this.neighbours = neighbours
updateTile()
}
}

View File

@@ -9,11 +9,10 @@ import android.service.quicksettings.Tile
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import be.mygod.vpnhotspot.LocalOnlyHotspotService import be.mygod.vpnhotspot.LocalOnlyHotspotService
import be.mygod.vpnhotspot.R import be.mygod.vpnhotspot.R
import be.mygod.vpnhotspot.util.KillableTileService
import be.mygod.vpnhotspot.util.stopAndUnbind import be.mygod.vpnhotspot.util.stopAndUnbind
@RequiresApi(26) @RequiresApi(26)
class LocalOnlyHotspotTileService : KillableTileService() { class LocalOnlyHotspotTileService : IpNeighbourMonitoringTileService() {
private val tile by lazy { Icon.createWithResource(application, R.drawable.ic_action_perm_scan_wifi) } private val tile by lazy { Icon.createWithResource(application, R.drawable.ic_action_perm_scan_wifi) }
private var binder: LocalOnlyHotspotService.Binder? = null private var binder: LocalOnlyHotspotService.Binder? = null
@@ -28,6 +27,24 @@ class LocalOnlyHotspotTileService : KillableTileService() {
super.onStopListening() super.onStopListening()
} }
override fun updateTile() {
val binder = binder ?: return
qsTile?.run {
icon = tile
subtitle(null)
val iface = binder.iface
if (iface.isNullOrEmpty()) {
state = Tile.STATE_INACTIVE
label = getText(R.string.tethering_temp_hotspot)
} else {
state = Tile.STATE_ACTIVE
label = binder.configuration?.ssid ?: getText(R.string.tethering_temp_hotspot)
subtitleDevices { it == iface }
}
updateTile()
}
}
override fun onClick() { override fun onClick() {
val binder = binder val binder = binder
when { when {
@@ -39,19 +56,7 @@ class LocalOnlyHotspotTileService : KillableTileService() {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) { override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
binder = service as LocalOnlyHotspotService.Binder binder = service as LocalOnlyHotspotService.Binder
service.ifaceChanged[this] = { service.ifaceChanged[this] = { updateTile() }
qsTile?.run {
icon = tile
if (it.isNullOrEmpty()) {
state = Tile.STATE_INACTIVE
label = getText(R.string.tethering_temp_hotspot)
} else {
state = Tile.STATE_ACTIVE
label = service.configuration?.ssid ?: getText(R.string.tethering_temp_hotspot)
}
updateTile()
}
}
super.onServiceConnected(name, service) super.onServiceConnected(name, service)
} }

View File

@@ -17,7 +17,6 @@ import be.mygod.vpnhotspot.net.TetherType
import be.mygod.vpnhotspot.net.TetheringManager import be.mygod.vpnhotspot.net.TetheringManager
import be.mygod.vpnhotspot.net.TetheringManager.tetheredIfaces import be.mygod.vpnhotspot.net.TetheringManager.tetheredIfaces
import be.mygod.vpnhotspot.net.wifi.WifiApManager import be.mygod.vpnhotspot.net.wifi.WifiApManager
import be.mygod.vpnhotspot.util.KillableTileService
import be.mygod.vpnhotspot.util.broadcastReceiver import be.mygod.vpnhotspot.util.broadcastReceiver
import be.mygod.vpnhotspot.util.readableMessage import be.mygod.vpnhotspot.util.readableMessage
import be.mygod.vpnhotspot.util.stopAndUnbind import be.mygod.vpnhotspot.util.stopAndUnbind
@@ -26,14 +25,14 @@ import java.io.IOException
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
@RequiresApi(24) @RequiresApi(24)
sealed class TetheringTileService : KillableTileService(), TetheringManager.StartTetheringCallback { sealed class TetheringTileService : IpNeighbourMonitoringTileService(), TetheringManager.StartTetheringCallback {
protected val tileOff by lazy { Icon.createWithResource(application, icon) } protected val tileOff by lazy { Icon.createWithResource(application, icon) }
protected val tileOn by lazy { Icon.createWithResource(application, R.drawable.ic_quick_settings_tile_on) } protected val tileOn by lazy { Icon.createWithResource(application, R.drawable.ic_quick_settings_tile_on) }
protected abstract val labelString: Int protected abstract val labelString: Int
protected abstract val tetherType: TetherType protected abstract val tetherType: TetherType
protected open val icon get() = tetherType.icon protected open val icon get() = tetherType.icon
protected var tethered: List<String>? = null private var tethered: List<String>? = null
protected val interested get() = tethered?.filter { TetherType.ofInterface(it) == tetherType } protected val interested get() = tethered?.filter { TetherType.ofInterface(it) == tetherType }
protected var binder: TetheringService.Binder? = null protected var binder: TetheringService.Binder? = null
@@ -73,8 +72,9 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
binder = null binder = null
} }
protected open fun updateTile() { override fun updateTile() {
qsTile?.run { qsTile?.run {
subtitle(null)
val interested = interested val interested = interested
when { when {
interested == null -> { interested == null -> {
@@ -89,6 +89,7 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
val binder = binder ?: return val binder = binder ?: return
state = Tile.STATE_ACTIVE state = Tile.STATE_ACTIVE
icon = if (interested.all(binder::isActive)) tileOn else tileOff icon = if (interested.all(binder::isActive)) tileOn else tileOff
subtitleDevices(interested::contains)
} }
} }
label = getText(labelString) label = getText(labelString)
@@ -172,6 +173,7 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
override fun updateTile() { override fun updateTile() {
qsTile?.run { qsTile?.run {
subtitle(null)
val interested = interested val interested = interested
if (interested == null) { if (interested == null) {
state = Tile.STATE_UNAVAILABLE state = Tile.STATE_UNAVAILABLE
@@ -181,6 +183,7 @@ sealed class TetheringTileService : KillableTileService(), TetheringManager.Star
val binder = binder ?: return val binder = binder ?: return
state = Tile.STATE_ACTIVE state = Tile.STATE_ACTIVE
icon = if (interested.isNotEmpty() && interested.all(binder::isActive)) tileOn else tileOff icon = if (interested.isNotEmpty() && interested.all(binder::isActive)) tileOn else tileOff
subtitleDevices(interested::contains)
} }
false -> { false -> {
state = Tile.STATE_INACTIVE state = Tile.STATE_INACTIVE

View File

@@ -46,7 +46,8 @@ enum class TetherType(@DrawableRes val icon: Int) {
.map { it.toPattern() } .map { it.toPattern() }
@RequiresApi(30) @RequiresApi(30)
private fun updateRegexs() { private fun updateRegexs() = synchronized(this) {
if (!requiresUpdate) return@synchronized
requiresUpdate = false requiresUpdate = false
TetheringManager.registerTetheringEventCallback(null, this) TetheringManager.registerTetheringEventCallback(null, this)
val tethering = "com.android.networkstack.tethering" to app.packageManager.getResourcesForApplication( val tethering = "com.android.networkstack.tethering" to app.packageManager.getResourcesForApplication(
@@ -59,7 +60,8 @@ enum class TetherType(@DrawableRes val icon: Int) {
} }
@RequiresApi(30) @RequiresApi(30)
override fun onTetherableInterfaceRegexpsChanged(args: Array<out Any?>?) { override fun onTetherableInterfaceRegexpsChanged(args: Array<out Any?>?) = synchronized(this) {
if (requiresUpdate) return@synchronized
Timber.i("onTetherableInterfaceRegexpsChanged: ${args?.contentToString()}") Timber.i("onTetherableInterfaceRegexpsChanged: ${args?.contentToString()}")
TetheringManager.unregisterTetheringEventCallback(this) TetheringManager.unregisterTetheringEventCallback(this)
requiresUpdate = true requiresUpdate = true
@@ -92,6 +94,7 @@ enum class TetherType(@DrawableRes val icon: Int) {
iface == null -> NONE iface == null -> NONE
iface == p2pDev -> WIFI_P2P iface == p2pDev -> WIFI_P2P
requiresUpdate -> { requiresUpdate -> {
Timber.d("requiresUpdate")
if (Build.VERSION.SDK_INT >= 30) updateRegexs() else error("unexpected requiresUpdate") if (Build.VERSION.SDK_INT >= 30) updateRegexs() else error("unexpected requiresUpdate")
ofInterface(iface, p2pDev) ofInterface(iface, p2pDev)
} }