2024-05-12 18:04:14 +03:00
|
|
|
|
package org.amnezia.vpn
|
|
|
|
|
|
|
|
|
|
|
|
import android.Manifest.permission
|
|
|
|
|
|
import android.annotation.SuppressLint
|
|
|
|
|
|
import android.app.Notification
|
|
|
|
|
|
import android.app.NotificationManager
|
|
|
|
|
|
import android.app.PendingIntent
|
|
|
|
|
|
import android.content.Context
|
|
|
|
|
|
import android.content.Intent
|
|
|
|
|
|
import android.content.pm.PackageManager
|
|
|
|
|
|
import android.os.Build
|
|
|
|
|
|
import androidx.core.app.NotificationChannelCompat.Builder
|
|
|
|
|
|
import androidx.core.app.NotificationCompat
|
|
|
|
|
|
import androidx.core.app.NotificationCompat.Action
|
|
|
|
|
|
import androidx.core.app.NotificationManagerCompat
|
|
|
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
|
|
import org.amnezia.vpn.protocol.ProtocolState
|
|
|
|
|
|
import org.amnezia.vpn.protocol.ProtocolState.CONNECTED
|
|
|
|
|
|
import org.amnezia.vpn.protocol.ProtocolState.DISCONNECTED
|
|
|
|
|
|
import org.amnezia.vpn.util.Log
|
|
|
|
|
|
import org.amnezia.vpn.util.net.TrafficStats.TrafficData
|
|
|
|
|
|
|
|
|
|
|
|
private const val TAG = "ServiceNotification"
|
|
|
|
|
|
|
|
|
|
|
|
private const val OLD_NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notification"
|
|
|
|
|
|
private const val NOTIFICATION_CHANNEL_ID: String = "org.amnezia.vpn.notifications"
|
|
|
|
|
|
const val NOTIFICATION_ID = 1337
|
|
|
|
|
|
|
|
|
|
|
|
private const val GET_ACTIVITY_REQUEST_CODE = 0
|
|
|
|
|
|
private const val CONNECT_REQUEST_CODE = 1
|
|
|
|
|
|
private const val DISCONNECT_REQUEST_CODE = 2
|
|
|
|
|
|
|
|
|
|
|
|
class ServiceNotification(private val context: Context) {
|
|
|
|
|
|
|
|
|
|
|
|
private val upDownSymbols = when (Build.BRAND) {
|
|
|
|
|
|
"Infinix" -> '˅' to '˄'
|
|
|
|
|
|
else -> '↓' to '↑'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private val notificationManager = NotificationManagerCompat.from(context)
|
|
|
|
|
|
|
|
|
|
|
|
private val notificationBuilder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
|
|
|
|
|
|
.setShowWhen(false)
|
|
|
|
|
|
.setOngoing(true)
|
|
|
|
|
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
|
|
|
|
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
|
|
|
|
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
|
|
|
|
|
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
|
|
|
|
|
.setContentIntent(
|
|
|
|
|
|
PendingIntent.getActivity(
|
|
|
|
|
|
context,
|
|
|
|
|
|
GET_ACTIVITY_REQUEST_CODE,
|
|
|
|
|
|
Intent(context, AmneziaActivity::class.java),
|
|
|
|
|
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
private val zeroSpeed: String = with(TrafficData.ZERO) {
|
|
|
|
|
|
formatSpeedString(rxString, txString)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-06-18 20:46:21 +03:00
|
|
|
|
fun buildNotification(serverName: String?, protocol: String?, state: ProtocolState): Notification {
|
2024-05-12 18:04:14 +03:00
|
|
|
|
val speedString = if (state == CONNECTED) zeroSpeed else null
|
|
|
|
|
|
|
|
|
|
|
|
Log.d(TAG, "Build notification: $serverName, $state")
|
|
|
|
|
|
|
|
|
|
|
|
return notificationBuilder
|
|
|
|
|
|
.setSmallIcon(R.drawable.ic_amnezia_round)
|
2024-06-18 20:46:21 +03:00
|
|
|
|
.setContentTitle((serverName ?: "AmneziaVPN") + (protocol?.let { " $it" } ?: ""))
|
2024-05-12 18:04:14 +03:00
|
|
|
|
.setContentText(context.getString(state))
|
|
|
|
|
|
.setSubText(speedString)
|
|
|
|
|
|
.setWhen(System.currentTimeMillis())
|
|
|
|
|
|
.clearActions()
|
|
|
|
|
|
.apply {
|
|
|
|
|
|
getAction(state)?.let {
|
|
|
|
|
|
addAction(it)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.build()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun buildNotification(speed: TrafficData): Notification =
|
|
|
|
|
|
notificationBuilder
|
|
|
|
|
|
.setWhen(System.currentTimeMillis())
|
|
|
|
|
|
.setSubText(getSpeedString(speed))
|
|
|
|
|
|
.build()
|
|
|
|
|
|
|
|
|
|
|
|
fun isNotificationEnabled(): Boolean {
|
|
|
|
|
|
if (!context.isNotificationPermissionGranted()) return false
|
|
|
|
|
|
if (!notificationManager.areNotificationsEnabled()) return false
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
|
|
|
return notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_ID)
|
|
|
|
|
|
?.let { it.importance != NotificationManager.IMPORTANCE_NONE } ?: true
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressLint("MissingPermission")
|
2024-06-18 20:46:21 +03:00
|
|
|
|
fun updateNotification(serverName: String?, protocol: String?, state: ProtocolState) {
|
2024-05-12 18:04:14 +03:00
|
|
|
|
if (context.isNotificationPermissionGranted()) {
|
|
|
|
|
|
Log.d(TAG, "Update notification: $serverName, $state")
|
2024-06-18 20:46:21 +03:00
|
|
|
|
notificationManager.notify(NOTIFICATION_ID, buildNotification(serverName, protocol, state))
|
2024-05-12 18:04:14 +03:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressLint("MissingPermission")
|
|
|
|
|
|
fun updateSpeed(speed: TrafficData) {
|
|
|
|
|
|
if (context.isNotificationPermissionGranted()) {
|
|
|
|
|
|
notificationManager.notify(NOTIFICATION_ID, buildNotification(speed))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private fun getSpeedString(traffic: TrafficData) =
|
|
|
|
|
|
if (traffic == TrafficData.ZERO) zeroSpeed
|
|
|
|
|
|
else formatSpeedString(traffic.rxString, traffic.txString)
|
|
|
|
|
|
|
|
|
|
|
|
private fun formatSpeedString(rx: String, tx: String) = with(upDownSymbols) { "$first $rx $second $tx" }
|
|
|
|
|
|
|
|
|
|
|
|
private fun getAction(state: ProtocolState): Action? {
|
|
|
|
|
|
return when (state) {
|
|
|
|
|
|
CONNECTED -> {
|
|
|
|
|
|
Action(
|
|
|
|
|
|
0, context.getString(R.string.disconnect),
|
|
|
|
|
|
PendingIntent.getBroadcast(
|
|
|
|
|
|
context,
|
|
|
|
|
|
DISCONNECT_REQUEST_CODE,
|
|
|
|
|
|
Intent(ACTION_DISCONNECT).apply {
|
2024-06-18 20:46:21 +03:00
|
|
|
|
setPackage(context.packageName)
|
2024-05-12 18:04:14 +03:00
|
|
|
|
},
|
|
|
|
|
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DISCONNECTED -> {
|
|
|
|
|
|
Action(
|
|
|
|
|
|
0, context.getString(R.string.connect),
|
2024-06-18 20:46:21 +03:00
|
|
|
|
PendingIntent.getBroadcast(
|
2024-05-12 18:04:14 +03:00
|
|
|
|
context,
|
|
|
|
|
|
CONNECT_REQUEST_CODE,
|
2024-06-18 20:46:21 +03:00
|
|
|
|
Intent(ACTION_CONNECT).apply {
|
|
|
|
|
|
setPackage(context.packageName)
|
|
|
|
|
|
},
|
2024-05-12 18:04:14 +03:00
|
|
|
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else -> null
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
|
fun createNotificationChannel(context: Context) {
|
|
|
|
|
|
with(NotificationManagerCompat.from(context)) {
|
|
|
|
|
|
deleteNotificationChannel(OLD_NOTIFICATION_CHANNEL_ID)
|
|
|
|
|
|
createNotificationChannel(
|
|
|
|
|
|
Builder(NOTIFICATION_CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_DEFAULT)
|
|
|
|
|
|
.setShowBadge(false)
|
|
|
|
|
|
.setSound(null, null)
|
|
|
|
|
|
.setVibrationEnabled(false)
|
|
|
|
|
|
.setLightsEnabled(false)
|
|
|
|
|
|
.setName("AmneziaVPN")
|
|
|
|
|
|
.setDescription(context.resources.getString(R.string.notificationChannelDescription))
|
|
|
|
|
|
.build()
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fun Context.isNotificationPermissionGranted(): Boolean =
|
|
|
|
|
|
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU ||
|
|
|
|
|
|
ContextCompat.checkSelfPermission(this, permission.POST_NOTIFICATIONS) ==
|
|
|
|
|
|
PackageManager.PERMISSION_GRANTED
|