Use Android 12 CallStyle notification for incoming calls

This commit is contained in:
Sylvain Berfini 2021-12-11 10:24:23 +01:00
parent b48b9e42ed
commit 58e2fc98aa
5 changed files with 116 additions and 35 deletions

View file

@ -34,11 +34,13 @@ import android.widget.RemoteViews
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import org.linphone.LinphoneApplication import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
import org.linphone.contact.Contact import org.linphone.contact.Contact
import org.linphone.core.Call import org.linphone.core.Call
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
import org.linphone.notifications.Notifiable
import org.linphone.notifications.NotificationsManager import org.linphone.notifications.NotificationsManager
import org.linphone.telecom.NativeCallWrapper import org.linphone.telecom.NativeCallWrapper
import org.linphone.utils.ImageUtils import org.linphone.utils.ImageUtils
@ -134,12 +136,14 @@ class Api26Compatibility {
return WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY return WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} }
fun createIncomingCallNotificationBuilder( fun createIncomingCallNotification(
context: Context, context: Context,
call: Call, call: Call,
notifiable: Notifiable,
pendingIntent: PendingIntent,
notificationsManager: NotificationsManager notificationsManager: NotificationsManager
): NotificationCompat.Builder { ): Notification {
val contact: Contact? = LinphoneApplication.coreContext.contactsManager.findContactByAddress(call.remoteAddress) val contact: Contact? = coreContext.contactsManager.findContactByAddress(call.remoteAddress)
val pictureUri = contact?.getContactThumbnailPictureUri() val pictureUri = contact?.getContactThumbnailPictureUri()
val roundPicture = ImageUtils.getRoundBitmapFromUri(context, pictureUri) val roundPicture = ImageUtils.getRoundBitmapFromUri(context, pictureUri)
val displayName = contact?.fullName ?: LinphoneUtils.getDisplayName(call.remoteAddress) val displayName = contact?.fullName ?: LinphoneUtils.getDisplayName(call.remoteAddress)
@ -154,7 +158,7 @@ class Api26Compatibility {
notificationLayoutHeadsUp.setImageViewBitmap(R.id.caller_picture, roundPicture) notificationLayoutHeadsUp.setImageViewBitmap(R.id.caller_picture, roundPicture)
} }
return NotificationCompat.Builder(context, context.getString(R.string.notification_channel_incoming_call_id)) val builder = NotificationCompat.Builder(context, context.getString(R.string.notification_channel_incoming_call_id))
.setStyle(NotificationCompat.DecoratedCustomViewStyle()) .setStyle(NotificationCompat.DecoratedCustomViewStyle())
.addPerson(notificationsManager.getPerson(contact, displayName, roundPicture)) .addPerson(notificationsManager.getPerson(contact, displayName, roundPicture))
.setSmallIcon(R.drawable.topbar_call_notification) .setSmallIcon(R.drawable.topbar_call_notification)
@ -168,7 +172,16 @@ class Api26Compatibility {
.setShowWhen(true) .setShowWhen(true)
.setOngoing(true) .setOngoing(true)
.setColor(ContextCompat.getColor(context, R.color.primary_color)) .setColor(ContextCompat.getColor(context, R.color.primary_color))
.setFullScreenIntent(pendingIntent, true)
.addAction(notificationsManager.getCallDeclineAction(notifiable))
.addAction(notificationsManager.getCallAnswerAction(notifiable))
.setCustomHeadsUpContentView(notificationLayoutHeadsUp) .setCustomHeadsUpContentView(notificationLayoutHeadsUp)
if (!corePreferences.preventInterfaceFromShowingUp) {
builder.setContentIntent(pendingIntent)
}
return builder.build()
} }
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")

View file

@ -20,7 +20,20 @@
package org.linphone.compatibility package org.linphone.compatibility
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.Notification
import android.app.PendingIntent import android.app.PendingIntent
import android.app.Person
import android.content.Context
import androidx.core.content.ContextCompat
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R
import org.linphone.contact.Contact
import org.linphone.core.Call
import org.linphone.notifications.Notifiable
import org.linphone.notifications.NotificationsManager
import org.linphone.utils.ImageUtils
import org.linphone.utils.LinphoneUtils
@TargetApi(31) @TargetApi(31)
class Api31Compatibility { class Api31Compatibility {
@ -28,5 +41,47 @@ class Api31Compatibility {
fun getUpdateCurrentPendingIntentFlag(): Int { fun getUpdateCurrentPendingIntentFlag(): Int {
return PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE return PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
} }
fun createIncomingCallNotification(
context: Context,
call: Call,
notifiable: Notifiable,
pendingIntent: PendingIntent,
notificationsManager: NotificationsManager
): Notification {
val contact: Contact? = coreContext.contactsManager.findContactByAddress(call.remoteAddress)
val pictureUri = contact?.getContactThumbnailPictureUri()
val roundPicture = ImageUtils.getRoundBitmapFromUri(context, pictureUri)
val displayName = contact?.fullName ?: LinphoneUtils.getDisplayName(call.remoteAddress)
val person = notificationsManager.getPerson(contact, displayName, roundPicture)
val caller = Person.Builder()
.setName(person.name)
.setIcon(person.icon?.toIcon(context))
.setUri(person.uri)
.setKey(person.key)
.setImportant(person.isImportant)
.build()
val declineIntent = notificationsManager.getCallDeclinePendingIntent(notifiable)
val answerIntent = notificationsManager.getCallAnswerPendingIntent(notifiable)
val builder = Notification.Builder(context, context.getString(R.string.notification_channel_incoming_call_id))
.setStyle(Notification.CallStyle.forIncomingCall(caller, declineIntent, answerIntent))
.setSmallIcon(R.drawable.topbar_call_notification)
.setCategory(Notification.CATEGORY_CALL)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setPriority(Notification.PRIORITY_HIGH)
.setWhen(System.currentTimeMillis())
.setAutoCancel(false)
.setShowWhen(true)
.setOngoing(true)
.setColor(ContextCompat.getColor(context, R.color.primary_color))
.setFullScreenIntent(pendingIntent, true)
if (!corePreferences.preventInterfaceFromShowingUp) {
builder.setContentIntent(pendingIntent)
}
return builder.build()
}
} }
} }

View file

@ -20,6 +20,8 @@
package org.linphone.compatibility package org.linphone.compatibility
import android.app.Activity import android.app.Activity
import android.app.Notification
import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
@ -30,7 +32,6 @@ import android.os.Vibrator
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import java.util.* import java.util.*
@ -38,6 +39,7 @@ import org.linphone.core.Call
import org.linphone.core.ChatRoom import org.linphone.core.ChatRoom
import org.linphone.core.Content import org.linphone.core.Content
import org.linphone.mediastream.Version import org.linphone.mediastream.Version
import org.linphone.notifications.Notifiable
import org.linphone.notifications.NotificationsManager import org.linphone.notifications.NotificationsManager
import org.linphone.telecom.NativeCallWrapper import org.linphone.telecom.NativeCallWrapper
@ -170,15 +172,19 @@ class Compatibility {
return WindowManager.LayoutParams.TYPE_PHONE return WindowManager.LayoutParams.TYPE_PHONE
} }
fun createIncomingCallNotificationBuilder( fun createIncomingCallNotification(
context: Context, context: Context,
call: Call, call: Call,
notifiable: Notifiable,
pendingIntent: PendingIntent,
notificationsManager: NotificationsManager notificationsManager: NotificationsManager
): NotificationCompat.Builder { ): Notification {
if (Build.MANUFACTURER.lowercase(Locale.getDefault()) == "xiaomi") { if (Version.sdkAboveOrEqual(Version.API31_ANDROID_12)) {
return XiaomiCompatibility.createIncomingCallNotificationBuilder(context, call, notificationsManager) return Api31Compatibility.createIncomingCallNotification(context, call, notifiable, pendingIntent, notificationsManager)
} else if (Build.MANUFACTURER.lowercase(Locale.getDefault()) == "xiaomi") {
return XiaomiCompatibility.createIncomingCallNotification(context, call, notifiable, pendingIntent, notificationsManager)
} }
return Api26Compatibility.createIncomingCallNotificationBuilder(context, call, notificationsManager) return Api26Compatibility.createIncomingCallNotification(context, call, notifiable, pendingIntent, notificationsManager)
} }
/* Call */ /* Call */

View file

@ -25,10 +25,12 @@ import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import org.linphone.LinphoneApplication import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
import org.linphone.contact.Contact import org.linphone.contact.Contact
import org.linphone.core.Call import org.linphone.core.Call
import org.linphone.notifications.Notifiable
import org.linphone.notifications.NotificationsManager import org.linphone.notifications.NotificationsManager
import org.linphone.utils.ImageUtils import org.linphone.utils.ImageUtils
import org.linphone.utils.LinphoneUtils import org.linphone.utils.LinphoneUtils
@ -36,18 +38,20 @@ import org.linphone.utils.LinphoneUtils
@TargetApi(26) @TargetApi(26)
class XiaomiCompatibility { class XiaomiCompatibility {
companion object { companion object {
fun createIncomingCallNotificationBuilder( fun createIncomingCallNotification(
context: Context, context: Context,
call: Call, call: Call,
notifiable: Notifiable,
pendingIntent: PendingIntent,
notificationsManager: NotificationsManager notificationsManager: NotificationsManager
): NotificationCompat.Builder { ): Notification {
val contact: Contact? = LinphoneApplication.coreContext.contactsManager.findContactByAddress(call.remoteAddress) val contact: Contact? = coreContext.contactsManager.findContactByAddress(call.remoteAddress)
val pictureUri = contact?.getContactThumbnailPictureUri() val pictureUri = contact?.getContactThumbnailPictureUri()
val roundPicture = ImageUtils.getRoundBitmapFromUri(context, pictureUri) val roundPicture = ImageUtils.getRoundBitmapFromUri(context, pictureUri)
val displayName = contact?.fullName ?: LinphoneUtils.getDisplayName(call.remoteAddress) val displayName = contact?.fullName ?: LinphoneUtils.getDisplayName(call.remoteAddress)
val address = LinphoneUtils.getDisplayableAddress(call.remoteAddress) val address = LinphoneUtils.getDisplayableAddress(call.remoteAddress)
return NotificationCompat.Builder(context, context.getString(R.string.notification_channel_incoming_call_id)) val builder = NotificationCompat.Builder(context, context.getString(R.string.notification_channel_incoming_call_id))
.addPerson(notificationsManager.getPerson(contact, displayName, roundPicture)) .addPerson(notificationsManager.getPerson(contact, displayName, roundPicture))
.setSmallIcon(R.drawable.topbar_call_notification) .setSmallIcon(R.drawable.topbar_call_notification)
.setLargeIcon(roundPicture ?: BitmapFactory.decodeResource(context.resources, R.drawable.avatar)) .setLargeIcon(roundPicture ?: BitmapFactory.decodeResource(context.resources, R.drawable.avatar))
@ -62,6 +66,15 @@ class XiaomiCompatibility {
.setShowWhen(true) .setShowWhen(true)
.setOngoing(true) .setOngoing(true)
.setColor(ContextCompat.getColor(context, R.color.primary_color)) .setColor(ContextCompat.getColor(context, R.color.primary_color))
.setFullScreenIntent(pendingIntent, true)
.addAction(notificationsManager.getCallDeclineAction(notifiable))
.addAction(notificationsManager.getCallAnswerAction(notifiable))
if (!corePreferences.preventInterfaceFromShowingUp) {
builder.setContentIntent(pendingIntent)
}
return builder.build()
} }
} }
} }

View file

@ -55,7 +55,7 @@ import org.linphone.utils.FileUtils
import org.linphone.utils.ImageUtils import org.linphone.utils.ImageUtils
import org.linphone.utils.LinphoneUtils import org.linphone.utils.LinphoneUtils
private class Notifiable(val notificationId: Int) { class Notifiable(val notificationId: Int) {
val messages: ArrayList<NotifiableMessage> = arrayListOf() val messages: ArrayList<NotifiableMessage> = arrayListOf()
var isGroup: Boolean = false var isGroup: Boolean = false
@ -66,7 +66,7 @@ private class Notifiable(val notificationId: Int) {
var dismissNotificationUponReadChatRoom: Boolean = true var dismissNotificationUponReadChatRoom: Boolean = true
} }
private class NotifiableMessage( class NotifiableMessage(
var message: String, var message: String,
val contact: Contact?, val contact: Contact?,
val sender: String, val sender: String,
@ -433,17 +433,7 @@ class NotificationsManager(private val context: Context) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
) )
val builder = Compatibility.createIncomingCallNotificationBuilder(context, call, this) val notification = Compatibility.createIncomingCallNotification(context, call, notifiable, pendingIntent, this)
builder
.setFullScreenIntent(pendingIntent, true)
.addAction(getCallDeclineAction(notifiable))
.addAction(getCallAnswerAction(notifiable))
if (!corePreferences.preventInterfaceFromShowingUp) {
builder.setContentIntent(pendingIntent)
}
val notification = builder.build()
Log.i("[Notifications Manager] Notifying incoming call notification [${notifiable.notificationId}]") Log.i("[Notifications Manager] Notifying incoming call notification [${notifiable.notificationId}]")
notify(notifiable.notificationId, notification) notify(notifiable.notificationId, notification)
@ -851,43 +841,47 @@ class NotificationsManager(private val context: Context) {
/* Notifications actions */ /* Notifications actions */
private fun getCallAnswerAction(notifiable: Notifiable): NotificationCompat.Action { fun getCallAnswerPendingIntent(notifiable: Notifiable): PendingIntent {
val answerIntent = Intent(context, NotificationBroadcastReceiver::class.java) val answerIntent = Intent(context, NotificationBroadcastReceiver::class.java)
answerIntent.action = INTENT_ANSWER_CALL_NOTIF_ACTION answerIntent.action = INTENT_ANSWER_CALL_NOTIF_ACTION
answerIntent.putExtra(INTENT_NOTIF_ID, notifiable.notificationId) answerIntent.putExtra(INTENT_NOTIF_ID, notifiable.notificationId)
answerIntent.putExtra(INTENT_REMOTE_ADDRESS, notifiable.remoteAddress) answerIntent.putExtra(INTENT_REMOTE_ADDRESS, notifiable.remoteAddress)
val answerPendingIntent = PendingIntent.getBroadcast( return PendingIntent.getBroadcast(
context, context,
notifiable.notificationId, notifiable.notificationId,
answerIntent, answerIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
) )
}
fun getCallAnswerAction(notifiable: Notifiable): NotificationCompat.Action {
return NotificationCompat.Action.Builder( return NotificationCompat.Action.Builder(
R.drawable.call_audio_start, R.drawable.call_audio_start,
context.getString(R.string.incoming_call_notification_answer_action_label), context.getString(R.string.incoming_call_notification_answer_action_label),
answerPendingIntent getCallAnswerPendingIntent(notifiable)
).build() ).build()
} }
private fun getCallDeclineAction(notifiable: Notifiable): NotificationCompat.Action { fun getCallDeclinePendingIntent(notifiable: Notifiable): PendingIntent {
val hangupIntent = Intent(context, NotificationBroadcastReceiver::class.java) val hangupIntent = Intent(context, NotificationBroadcastReceiver::class.java)
hangupIntent.action = INTENT_HANGUP_CALL_NOTIF_ACTION hangupIntent.action = INTENT_HANGUP_CALL_NOTIF_ACTION
hangupIntent.putExtra(INTENT_NOTIF_ID, notifiable.notificationId) hangupIntent.putExtra(INTENT_NOTIF_ID, notifiable.notificationId)
hangupIntent.putExtra(INTENT_REMOTE_ADDRESS, notifiable.remoteAddress) hangupIntent.putExtra(INTENT_REMOTE_ADDRESS, notifiable.remoteAddress)
val hangupPendingIntent = PendingIntent.getBroadcast( return PendingIntent.getBroadcast(
context, context,
notifiable.notificationId, notifiable.notificationId,
hangupIntent, hangupIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
) )
}
fun getCallDeclineAction(notifiable: Notifiable): NotificationCompat.Action {
return NotificationCompat.Action.Builder( return NotificationCompat.Action.Builder(
R.drawable.call_hangup, R.drawable.call_hangup,
context.getString(R.string.incoming_call_notification_hangup_action_label), context.getString(R.string.incoming_call_notification_hangup_action_label),
hangupPendingIntent getCallDeclinePendingIntent(notifiable)
).build() ).build()
} }