Use one notification for two services

This commit is contained in:
Mygod
2018-01-21 12:11:35 -08:00
parent d8b4a772ee
commit ac82a9446c
6 changed files with 89 additions and 66 deletions

View File

@@ -1,13 +1,8 @@
package be.mygod.vpnhotspot
import android.annotation.TargetApi
import android.app.Application
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.SharedPreferences
import android.content.res.Configuration
import android.os.Build
import android.os.Handler
import android.preference.PreferenceManager
import android.support.annotation.StringRes
@@ -23,26 +18,12 @@ class App : Application() {
override fun onCreate() {
super.onCreate()
app = this
updateNotificationChannels()
ServiceNotification.updateNotificationChannels()
}
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
updateNotificationChannels()
}
private fun updateNotificationChannels() {
if (Build.VERSION.SDK_INT >= 26) @TargetApi(26) {
val nm = getSystemService(NotificationManager::class.java)
val tethering = NotificationChannel(TetheringService.CHANNEL,
getText(R.string.notification_channel_tethering), NotificationManager.IMPORTANCE_LOW)
tethering.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
nm.createNotificationChannels(listOf(
NotificationChannel(RepeaterService.CHANNEL, getText(R.string.notification_channel_repeater),
NotificationManager.IMPORTANCE_LOW), tethering
))
nm.deleteNotificationChannel("hotspot") // remove old service channel
}
ServiceNotification.updateNotificationChannels()
}
val handler = Handler()

View File

@@ -79,7 +79,7 @@ class RepeaterFragment : Fragment(), ServiceConnection, Toolbar.OnMenuItemClickL
val ip = neighbour?.ip
val icon get() = TetherType.ofInterface(iface, p2pInterface).icon
val title get() = listOf(ip, mac).filter { !it.isNullOrEmpty() }.joinToString(", ")
val title get() = listOf(ip, mac).filter { !it.isNullOrEmpty() }.joinToString()
val description get() = when (neighbour?.state) {
IpNeighbour.State.INCOMPLETE, null -> "Connecting to $iface"
IpNeighbour.State.VALID -> "Connected to $iface"

View File

@@ -1,6 +1,5 @@
package be.mygod.vpnhotspot
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
@@ -13,8 +12,6 @@ import android.os.Binder
import android.os.Handler
import android.os.Looper
import android.support.annotation.StringRes
import android.support.v4.app.NotificationCompat
import android.support.v4.content.ContextCompat
import android.support.v4.content.LocalBroadcastManager
import android.util.Log
import android.widget.Toast
@@ -26,8 +23,6 @@ import java.util.regex.Pattern
class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Callback {
companion object {
const val CHANNEL = "repeater"
const val CHANNEL_ID = 1
const val ACTION_STATUS_CHANGED = "be.mygod.vpnhotspot.RepeaterService.STATUS_CHANGED"
const val KEY_NET_ID = "netId"
private const val TAG = "RepeaterService"
@@ -304,19 +299,8 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
}
}
private fun showNotification(group: WifiP2pGroup? = null) {
val builder = NotificationCompat.Builder(this, CHANNEL)
.setWhen(0)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setContentTitle(group?.networkName ?: ssid ?: getString(R.string.repeater_connecting))
.setSmallIcon(R.drawable.ic_device_wifi_tethering)
.setContentIntent(PendingIntent.getActivity(this, 0,
Intent(this, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
val size = group?.clientList?.size ?: 0
if (size != 0) builder.setContentText(resources.getQuantityString(R.plurals.notification_connected_devices,
size, size, group!!.`interface`))
startForeground(CHANNEL_ID, builder.build())
}
private fun showNotification(group: WifiP2pGroup? = null) = ServiceNotification.startForeground(this,
if (group == null) emptyMap() else mapOf(Pair(group.`interface`, group.clientList?.size ?: 0)))
private fun removeGroup() {
p2pManager.removeGroup(channel, object : WifiP2pManager.ActionListener {
@@ -345,7 +329,7 @@ class RepeaterService : Service(), WifiP2pManager.ChannelListener, VpnMonitor.Ca
Toast.makeText(this, getText(R.string.noisy_su_failure), Toast.LENGTH_SHORT).show()
routing = null
status = Status.IDLE
stopForeground(true)
ServiceNotification.stopForeground(this)
stopSelf()
}

View File

@@ -0,0 +1,76 @@
package be.mygod.vpnhotspot
import android.annotation.TargetApi
import android.app.*
import android.content.Context
import android.content.Intent
import android.os.Build
import android.support.v4.app.NotificationCompat
import android.support.v4.content.ContextCompat
import be.mygod.vpnhotspot.App.Companion.app
object ServiceNotification {
private const val CHANNEL = "tethering"
private const val CHANNEL_ID = 1
private val deviceCountsMap = HashMap<Service, Map<String, Int>>()
private val manager = app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private fun buildNotification(context: Context): Notification {
val builder = NotificationCompat.Builder(context, CHANNEL)
.setWhen(0)
.setColor(ContextCompat.getColor(context, R.color.colorPrimary))
.setContentTitle("VPN tethering active")
.setSmallIcon(R.drawable.ic_device_wifi_tethering)
.setContentIntent(PendingIntent.getActivity(context, 0,
Intent(context, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
.setVisibility(Notification.VISIBILITY_PUBLIC)
val deviceCounts = deviceCountsMap.values.flatMap { it.entries }.sortedBy { it.key }
return when (deviceCounts.size) {
0 -> builder.build()
1 -> {
val (dev, size) = deviceCounts.single()
builder.setContentText(context.resources.getQuantityString(R.plurals.notification_connected_devices,
size, size, dev))
.build()
}
else -> {
val deviceCount = deviceCounts.sumBy { it.value }
NotificationCompat.BigTextStyle(builder
.setContentText(context.resources.getQuantityString(R.plurals.notification_connected_devices,
deviceCount, deviceCount,
context.resources.getQuantityString(R.plurals.notification_interfaces,
deviceCounts.size, deviceCounts.size))))
.bigText(deviceCounts.joinToString("\n") { (dev, size) ->
context.resources.getQuantityString(R.plurals.notification_connected_devices,
size, size, dev)
})
.build()
}
}
}
fun startForeground(service: Service, deviceCounts: Map<String, Int>) {
deviceCountsMap[service] = deviceCounts
service.startForeground(CHANNEL_ID, buildNotification(service))
}
fun stopForeground(service: Service) {
deviceCountsMap.remove(service)
if (deviceCountsMap.isEmpty()) service.stopForeground(true) else {
service.stopForeground(false)
manager.notify(CHANNEL_ID, buildNotification(service))
}
}
fun updateNotificationChannels() {
if (Build.VERSION.SDK_INT >= 26) @TargetApi(26) {
val tethering = NotificationChannel(CHANNEL,
app.getText(R.string.notification_channel_tethering), NotificationManager.IMPORTANCE_LOW)
tethering.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
manager.createNotificationChannel(tethering)
// remove old service channels
manager.deleteNotificationChannel("hotspot")
manager.deleteNotificationChannel("repeater")
}
}
}

View File

@@ -1,12 +1,8 @@
package be.mygod.vpnhotspot
import android.app.Notification
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.support.v4.app.NotificationCompat
import android.support.v4.content.ContextCompat
import android.support.v4.content.LocalBroadcastManager
import android.widget.Toast
import be.mygod.vpnhotspot.App.Companion.app
@@ -14,8 +10,6 @@ import be.mygod.vpnhotspot.net.*
class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Callback {
companion object {
const val CHANNEL = "tethering"
const val CHANNEL_ID = 2
const val EXTRA_ADD_INTERFACE = "interface.add"
const val EXTRA_REMOVE_INTERFACE = "interface.remove"
}
@@ -46,7 +40,7 @@ class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Call
private fun updateRoutings() {
if (routings.isEmpty()) {
unregisterReceiver()
stopForeground(true)
ServiceNotification.stopForeground(this)
stopSelf()
} else {
val upstream = upstream
@@ -106,23 +100,8 @@ class TetheringService : Service(), VpnMonitor.Callback, IpNeighbourMonitor.Call
this.neighbours = neighbours.values.toList()
}
override fun postIpNeighbourAvailable() {
val builder = NotificationCompat.Builder(this, CHANNEL)
.setWhen(0)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setContentTitle("VPN tethering active")
.setSmallIcon(R.drawable.ic_device_wifi_tethering)
.setContentIntent(PendingIntent.getActivity(this, 0,
Intent(this, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT))
.setVisibility(Notification.VISIBILITY_PUBLIC)
val content = neighbours.groupBy { it.dev }
.filter { (dev, _) -> routings.contains(dev) }
.mapValues { (_, neighbours) -> neighbours.size }
.toList()
.joinToString(", ") { (dev, size) ->
resources.getQuantityString(R.plurals.notification_connected_devices, size, size, dev)
}
if (content.isNotEmpty()) builder.setContentText(content)
startForeground(CHANNEL_ID, builder.build())
val sizeLookup = neighbours.groupBy { it.dev }.mapValues { (_, neighbours) -> neighbours.size }
ServiceNotification.startForeground(this, routings.keys.associate { Pair(it, sizeLookup[it] ?: 0) })
}
override fun onDestroy() {

View File

@@ -51,12 +51,15 @@
<string name="settings_misc_donate">Donate</string>
<string name="settings_misc_donate_summary">I love money</string>
<string name="notification_channel_repeater">Repeater Service</string>
<string name="notification_channel_tethering">VPN Tethering Service</string>
<plurals name="notification_connected_devices">
<item quantity="one">%d device connected to %s</item>
<item quantity="other">%d devices connected to %s</item>
</plurals>
<plurals name="notification_interfaces">
<item quantity="one">%d interface</item>
<item quantity="other">%d interfaces</item>
</plurals>
<string name="exception_interface_not_found">Fatal: Downstream interface not found</string>
<string name="noisy_su_failure">Something went wrong, please check logcat.</string>