2020-10-01 22:20:19 +02:00
package com.geeksville.mesh.service
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
2021-02-05 09:31:25 +08:00
import android.graphics.Bitmap
import android.graphics.Canvas
2020-10-01 22:20:19 +02:00
import android.graphics.Color
2021-02-07 17:38:54 -08:00
import android.media.AudioAttributes
import android.media.RingtoneManager
2020-10-01 22:20:19 +02:00
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
2021-02-05 09:31:25 +08:00
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
2020-10-01 22:20:19 +02:00
import com.geeksville.mesh.DataPacket
import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R
import com.geeksville.mesh.android.notificationManager
2021-02-07 17:38:54 -08:00
import com.geeksville.mesh.ui.SLogging
2020-10-01 22:20:19 +02:00
import com.geeksville.mesh.utf8
2021-02-05 09:31:25 +08:00
import java.io.Closeable
2020-10-01 22:20:19 +02:00
class MeshServiceNotifications (
private val context : Context
2021-02-06 09:29:11 +08:00
) : Closeable {
2020-10-01 22:20:19 +02:00
private val notificationManager : NotificationManager get ( ) = context . notificationManager
2021-02-07 17:38:54 -08:00
// We have two notification channels: one for general service status and another one for messages
2020-10-01 22:20:19 +02:00
val notifyId = 101
2021-02-07 17:38:54 -08:00
private val messageNotifyId = 102
2021-02-05 09:31:25 +08:00
private var largeIcon : Bitmap ? = null
2020-10-01 22:20:19 +02:00
@RequiresApi ( Build . VERSION_CODES . O )
private fun createNotificationChannel ( ) : String {
val channelId = " my_service "
val channelName = context . getString ( R . string . meshtastic _service _notifications )
2021-02-05 09:31:25 +08:00
val channel = NotificationChannel (
channelId ,
channelName ,
2021-02-07 17:38:54 -08:00
NotificationManager . IMPORTANCE _MIN
2021-02-05 09:31:25 +08:00
) . apply {
2020-10-01 22:20:19 +02:00
lightColor = Color . BLUE
lockscreenVisibility = Notification . VISIBILITY _PRIVATE
}
notificationManager . createNotificationChannel ( channel )
return channelId
}
2021-02-07 17:38:54 -08:00
@RequiresApi ( Build . VERSION_CODES . O )
private fun createMessageNotificationChannel ( ) : String {
val channelId = " my_messages "
val channelName = context . getString ( R . string . meshtastic _messages _notifications )
val channel = NotificationChannel (
channelId ,
channelName ,
NotificationManager . IMPORTANCE _HIGH
) . apply {
lightColor = Color . BLUE
lockscreenVisibility = Notification . VISIBILITY _PUBLIC
setShowBadge ( true )
setSound (
RingtoneManager . getDefaultUri ( RingtoneManager . TYPE _NOTIFICATION ) ,
AudioAttributes . Builder ( )
. setUsage ( AudioAttributes . USAGE _NOTIFICATION )
. setContentType ( AudioAttributes . CONTENT _TYPE _SONIFICATION )
. build ( )
)
}
notificationManager . createNotificationChannel ( channel )
return channelId
}
2020-10-01 22:20:19 +02:00
private val channelId : String by lazy {
if ( Build . VERSION . SDK _INT >= Build . VERSION_CODES . O ) {
createNotificationChannel ( )
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
" "
}
}
2021-02-07 17:38:54 -08:00
private val messageChannelId : String by lazy {
if ( Build . VERSION . SDK _INT >= Build . VERSION_CODES . O ) {
createMessageNotificationChannel ( )
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
" "
}
2020-10-01 22:20:19 +02:00
}
2021-02-07 17:38:54 -08:00
fun updateServiceStateNotification ( summaryString : String ) =
notificationManager . notify ( notifyId ,
createServiceStateNotification ( summaryString ) )
fun updateMessageNotification ( name : String , message : String ) =
notificationManager . notify ( messageNotifyId ,
createMessageNotifcation ( name , message ) )
2020-10-01 22:20:19 +02:00
private val openAppIntent : PendingIntent by lazy {
PendingIntent . getActivity ( context , 0 , Intent ( context , MainActivity :: class . java ) , 0 )
}
2021-02-05 09:31:25 +08:00
/ * *
* Generate a bitmap from a vector drawable ( even on old builds )
* https : //stackoverflow.com/questions/33696488/getting-bitmap-from-vector-drawable
* /
fun getBitmapFromVectorDrawable ( drawableId : Int ) : Bitmap {
var drawable = ContextCompat . getDrawable ( context , drawableId ) !!
if ( Build . VERSION . SDK _INT < Build . VERSION_CODES . LOLLIPOP ) {
drawable = DrawableCompat . wrap ( drawable ) . mutate ( )
}
val bitmap = Bitmap . createBitmap (
drawable . intrinsicWidth ,
drawable . intrinsicHeight , Bitmap . Config . ARGB _8888
)
val canvas = Canvas ( bitmap )
drawable . setBounds ( 0 , 0 , canvas . width , canvas . height )
drawable . draw ( canvas )
return bitmap
}
2021-02-07 17:38:54 -08:00
fun commonBuilder ( channel : String ) : NotificationCompat . Builder {
val builder = NotificationCompat . Builder ( context , channel )
2020-10-01 22:20:19 +02:00
. setVisibility ( NotificationCompat . VISIBILITY _PUBLIC )
. setContentIntent ( openAppIntent )
2021-02-06 09:29:11 +08:00
// Set the notification icon
if ( Build . VERSION . SDK _INT <= Build . VERSION_CODES . LOLLIPOP _MR1 ) {
// If running on really old versions of android (<= 5.1.1) (possibly only cyanogen) we might encounter a bug with setting application specific icons
// so punt and stay with just the bluetooth icon - see https://meshtastic.discourse.group/t/android-1-1-42-ready-for-alpha-testing/2399/3?u=geeksville
builder . setSmallIcon ( android . R . drawable . stat _sys _data _bluetooth )
} else {
// Newer androids also support a 'large' icon
// We delay making this bitmap until we know we need it
if ( largeIcon == null )
largeIcon = getBitmapFromVectorDrawable ( R . mipmap . ic _launcher2 )
builder . setSmallIcon ( if ( Build . VERSION . SDK _INT < Build . VERSION_CODES . N ) R . drawable . app _icon _novect else R . drawable . app _icon ) // vector form icons don't work reliably on older androids
. setLargeIcon ( largeIcon )
}
2021-02-07 17:38:54 -08:00
return builder
}
2021-02-06 09:29:11 +08:00
2021-02-07 17:38:54 -08:00
fun createServiceStateNotification ( summaryString : String ) : Notification {
val builder = commonBuilder ( channelId )
with ( builder ) {
2021-02-08 13:25:29 +08:00
priority = NotificationCompat . PRIORITY _MIN
2021-02-07 17:38:54 -08:00
setCategory ( Notification . CATEGORY _SERVICE )
setOngoing ( true )
setContentTitle ( summaryString ) // leave this off for now so our notification looks smaller
}
return builder . build ( )
}
2020-10-01 22:20:19 +02:00
2021-02-07 17:38:54 -08:00
fun createMessageNotifcation ( name : String , message : String ) : Notification {
val builder = commonBuilder ( messageChannelId )
with ( builder ) {
2021-02-08 13:25:29 +08:00
priority = NotificationCompat . PRIORITY _DEFAULT
2021-02-07 17:38:54 -08:00
setCategory ( Notification . CATEGORY _MESSAGE )
setAutoCancel ( true )
setContentTitle ( name )
2021-02-08 13:25:29 +08:00
setContentText ( message )
2021-02-07 17:38:54 -08:00
setStyle (
2020-10-01 22:20:19 +02:00
NotificationCompat . BigTextStyle ( )
2021-02-07 17:38:54 -08:00
. bigText ( message ) ,
2020-10-01 22:20:19 +02:00
)
}
return builder . build ( )
}
2021-02-05 09:31:25 +08:00
override fun close ( ) {
largeIcon ?. recycle ( )
largeIcon = null
}
2020-10-01 22:20:19 +02:00
}