diff --git a/CHANGELOG.md b/CHANGELOG.md index 1081df1d9..30f338611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/app/src/main/java/org/linphone/compatibility/Api26Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Api26Compatibility.kt index 2f6da50f4..9a541f59e 100644 --- a/app/src/main/java/org/linphone/compatibility/Api26Compatibility.kt +++ b/app/src/main/java/org/linphone/compatibility/Api26Compatibility.kt @@ -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) diff --git a/app/src/main/java/org/linphone/compatibility/Api31Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Api31Compatibility.kt index c5ab3b1e0..7ca6dde46 100644 --- a/app/src/main/java/org/linphone/compatibility/Api31Compatibility.kt +++ b/app/src/main/java/org/linphone/compatibility/Api31Compatibility.kt @@ -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() + } } } diff --git a/app/src/main/java/org/linphone/compatibility/Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Compatibility.kt index db62fba0b..4a6a2e3e4 100644 --- a/app/src/main/java/org/linphone/compatibility/Compatibility.kt +++ b/app/src/main/java/org/linphone/compatibility/Compatibility.kt @@ -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 { diff --git a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt index 94bca1ff6..0d33add8a 100644 --- a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt +++ b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt @@ -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)