Use Android 12 CallStyle notification for ongoing calls

This commit is contained in:
Sylvain Berfini 2021-12-11 16:14:12 +01:00
parent 58e2fc98aa
commit 57b0255cc4
5 changed files with 149 additions and 59 deletions

View file

@ -22,6 +22,7 @@ Group changes to describe their impact on the project, as follows:
- New video call UI on foldable device like Galaxy Z Fold
- Setting to automatically record all calls
- When using a physical keyboard, use left control + enter keys to send message
- Using CallStyle notifications for calls for devices running Android 12 or newer
### Changed
- UI has been reworked around SlidingPane component to better handle tablets & foldable devices
@ -38,6 +39,7 @@ Group changes to describe their impact on the project, as follows:
- "Infinite backstack", now each view is stored (at most) once in the backstack
- Going back to the dialer when pressing back in a chat room after clicking on a chat message notification
- Missing international prefix / phone number in assistant after granting permission
- Display issue for incoming call notification preventing to use answer/hangup actions on some Xiaomi devices (like Redmi Note 9S)
### Removed
- Launcher Activity has been replaced by [Splash Screen API](https://developer.android.com/reference/kotlin/androidx/core/splashscreen/SplashScreen)

View file

@ -184,6 +184,69 @@ class Api26Compatibility {
return builder.build()
}
fun createCallNotification(
context: Context,
call: Call,
notifiable: Notifiable,
pendingIntent: PendingIntent,
channel: String,
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 stringResourceId: Int
val iconResourceId: Int
when (call.state) {
Call.State.Paused, Call.State.Pausing, Call.State.PausedByRemote -> {
stringResourceId = R.string.call_notification_paused
iconResourceId = R.drawable.topbar_call_paused_notification
}
Call.State.OutgoingRinging, Call.State.OutgoingProgress, Call.State.OutgoingInit, Call.State.OutgoingEarlyMedia -> {
stringResourceId = R.string.call_notification_outgoing
iconResourceId = if (call.params.videoEnabled()) {
R.drawable.topbar_videocall_notification
} else {
R.drawable.topbar_call_notification
}
}
else -> {
stringResourceId = R.string.call_notification_active
iconResourceId = if (call.currentParams.videoEnabled()) {
R.drawable.topbar_videocall_notification
} else {
R.drawable.topbar_call_notification
}
}
}
val builder = NotificationCompat.Builder(
context, channel
)
.setContentTitle(contact?.fullName ?: displayName)
.setContentText(context.getString(stringResourceId))
.setSmallIcon(iconResourceId)
.setLargeIcon(roundPicture)
.addPerson(notificationsManager.getPerson(contact, displayName, roundPicture))
.setAutoCancel(false)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
.setOngoing(true)
.setColor(ContextCompat.getColor(context, R.color.notification_led_color))
.addAction(notificationsManager.getCallDeclineAction(notifiable))
if (!corePreferences.preventInterfaceFromShowingUp) {
builder.setContentIntent(pendingIntent)
}
return builder.build()
}
@SuppressLint("MissingPermission")
fun eventVibration(vibrator: Vibrator) {
val effect = VibrationEffect.createWaveform(longArrayOf(0L, 100L, 100L), intArrayOf(0, VibrationEffect.DEFAULT_AMPLITUDE, 0), -1)

View file

@ -69,7 +69,6 @@ class Api31Compatibility {
.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)
@ -83,5 +82,62 @@ class Api31Compatibility {
return builder.build()
}
fun createCallNotification(
context: Context,
call: Call,
notifiable: Notifiable,
pendingIntent: PendingIntent,
channel: String,
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 isVideo = call.currentParams.videoEnabled()
val iconResourceId: Int = when (call.state) {
Call.State.Paused, Call.State.Pausing, Call.State.PausedByRemote -> {
R.drawable.topbar_call_paused_notification
}
else -> {
if (isVideo) {
R.drawable.topbar_videocall_notification
} else {
R.drawable.topbar_call_notification
}
}
}
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 builder = Notification.Builder(
context, channel
)
.setStyle(Notification.CallStyle.forOngoingCall(caller, declineIntent).setIsVideo(isVideo))
.setSmallIcon(iconResourceId)
.setAutoCancel(false)
.setCategory(Notification.CATEGORY_CALL)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
.setOngoing(true)
.setColor(ContextCompat.getColor(context, R.color.notification_led_color))
.setFullScreenIntent(pendingIntent, true) // This is required for CallStyle notification
if (!corePreferences.preventInterfaceFromShowingUp) {
builder.setContentIntent(pendingIntent)
}
return builder.build()
}
}
}

View file

@ -187,6 +187,20 @@ class Compatibility {
return Api26Compatibility.createIncomingCallNotification(context, call, notifiable, pendingIntent, notificationsManager)
}
fun createCallNotification(
context: Context,
call: Call,
notifiable: Notifiable,
pendingIntent: PendingIntent,
channel: String,
notificationsManager: NotificationsManager
): Notification {
if (Version.sdkAboveOrEqual(Version.API31_ANDROID_12)) {
return Api31Compatibility.createCallNotification(context, call, notifiable, pendingIntent, channel, notificationsManager)
}
return Api26Compatibility.createCallNotification(context, call, notifiable, pendingIntent, channel, notificationsManager)
}
/* Call */
fun canDrawOverlay(context: Context): Boolean {

View file

@ -482,7 +482,6 @@ class NotificationsManager(private val context: Context) {
}
val notification = builder.build()
notify(MISSED_CALLS_NOTIF_ID, notification, MISSED_CALL_TAG)
}
@ -493,6 +492,18 @@ class NotificationsManager(private val context: Context) {
fun displayCallNotification(call: Call, useAsForeground: Boolean = false) {
val notifiable = getNotifiableForCall(call)
val callActivity: Class<*> = when (call.state) {
Call.State.Paused, Call.State.Pausing, Call.State.PausedByRemote -> {
CallActivity::class.java
}
Call.State.OutgoingRinging, Call.State.OutgoingProgress, Call.State.OutgoingInit, Call.State.OutgoingEarlyMedia -> {
OutgoingCallActivity::class.java
}
else -> {
CallActivity::class.java
}
}
val serviceChannel = context.getString(R.string.notification_channel_service_id)
val channelToUse = when (val serviceChannelImportance = Compatibility.getChannelImportance(notificationManager, serviceChannel)) {
NotificationManagerCompat.IMPORTANCE_NONE -> {
@ -510,40 +521,6 @@ class NotificationsManager(private val context: Context) {
}
}
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 stringResourceId: Int
val iconResourceId: Int
val callActivity: Class<*>
when (call.state) {
Call.State.Paused, Call.State.Pausing, Call.State.PausedByRemote -> {
callActivity = CallActivity::class.java
stringResourceId = R.string.call_notification_paused
iconResourceId = R.drawable.topbar_call_paused_notification
}
Call.State.OutgoingRinging, Call.State.OutgoingProgress, Call.State.OutgoingInit, Call.State.OutgoingEarlyMedia -> {
callActivity = OutgoingCallActivity::class.java
stringResourceId = R.string.call_notification_outgoing
iconResourceId = if (call.params.videoEnabled()) {
R.drawable.topbar_videocall_notification
} else {
R.drawable.topbar_call_notification
}
}
else -> {
callActivity = CallActivity::class.java
stringResourceId = R.string.call_notification_active
iconResourceId = if (call.currentParams.videoEnabled()) {
R.drawable.topbar_videocall_notification
} else {
R.drawable.topbar_call_notification
}
}
}
val callNotificationIntent = Intent(context, callActivity)
callNotificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val pendingIntent = PendingIntent.getActivity(
@ -553,29 +530,7 @@ class NotificationsManager(private val context: Context) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val builder = NotificationCompat.Builder(
context, channelToUse
)
.setContentTitle(contact?.fullName ?: displayName)
.setContentText(context.getString(stringResourceId))
.setSmallIcon(iconResourceId)
.setLargeIcon(roundPicture)
.addPerson(getPerson(contact, displayName, roundPicture))
.setAutoCancel(false)
.setCategory(NotificationCompat.CATEGORY_CALL)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
.setOngoing(true)
.setColor(ContextCompat.getColor(context, R.color.notification_led_color))
.addAction(getCallDeclineAction(notifiable))
if (!corePreferences.preventInterfaceFromShowingUp) {
builder.setContentIntent(pendingIntent)
}
val notification = builder.build()
val notification = Compatibility.createCallNotification(context, call, notifiable, pendingIntent, channelToUse, this)
Log.i("[Notifications Manager] Notifying call notification [${notifiable.notificationId}]")
notify(notifiable.notificationId, notification)