From 0a8069ada2b9855ef3c05b3a70ba4df09a7272fe Mon Sep 17 00:00:00 2001
From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com>
Date: Mon, 5 Dec 2022 16:13:48 +0300
Subject: [PATCH] android: Notification sound (#1468)
* android: fix full screen call notification (#1466)
* android: Closing call means canceling notification too
* show full screen call when screen is off OR locked
* make notification non-silent and set category
* remove call notification category
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
* android: Notification sound
* Log
* Ringtone channel
* rename call channel
* Non-hideable headsUp notification and reject button
* Removed LockScreenCallChannel
* call channel name
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
---
.../java/chat/simplex/app/model/NtfManager.kt | 54 ++++++++++++-------
.../app/views/call/IncomingCallActivity.kt | 33 ++++++------
.../app/src/main/res/values-de/strings.xml | 1 -
.../app/src/main/res/values-ru/strings.xml | 1 -
.../app/src/main/res/values/strings.xml | 1 -
5 files changed, 54 insertions(+), 36 deletions(-)
diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt b/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt
index b90b6cc302..36963e3e4b 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt
@@ -25,9 +25,9 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference
const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS"
// DO NOT change notification channel settings / names
- const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION"
- const val LockScreenCallChannel: String = "chat.simplex.app.LOCK_SCREEN_CALL_NOTIFICATION"
+ const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION_1"
const val AcceptCallAction: String = "chat.simplex.app.ACCEPT_CALL"
+ const val RejectCallAction: String = "chat.simplex.app.REJECT_CALL"
const val CallNotificationId: Int = -1
private const val ChatIdKey: String = "chatId"
@@ -39,19 +39,21 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference
init {
manager.createNotificationChannel(NotificationChannel(MessageChannel, generalGetString(R.string.ntf_channel_messages), NotificationManager.IMPORTANCE_HIGH))
- manager.createNotificationChannel(NotificationChannel(LockScreenCallChannel, generalGetString(R.string.ntf_channel_calls_lockscreen), NotificationManager.IMPORTANCE_HIGH))
- manager.createNotificationChannel(callNotificationChannel())
+ manager.createNotificationChannel(callNotificationChannel(CallChannel, generalGetString(R.string.ntf_channel_calls)))
+ // Remove old channels since they can't be edited
+ manager.deleteNotificationChannel("chat.simplex.app.CALL_NOTIFICATION")
+ manager.deleteNotificationChannel("chat.simplex.app.LOCK_SCREEN_CALL_NOTIFICATION")
}
enum class NotificationAction {
ACCEPT_CONTACT_REQUEST
}
- private fun callNotificationChannel(): NotificationChannel {
- val callChannel = NotificationChannel(CallChannel, generalGetString(R.string.ntf_channel_calls), NotificationManager.IMPORTANCE_HIGH)
+ private fun callNotificationChannel(channelId: String, channelName: String): NotificationChannel {
+ val callChannel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH)
val attrs = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_NOTIFICATION)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.build()
val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + R.raw.ring_once)
Log.d(TAG,"callNotificationChannel sound: $soundUri")
@@ -153,13 +155,14 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference
}
fun notifyCallInvitation(invitation: RcvCallInvitation) {
- if (SimplexApp.context.isAppOnForeground) return
val keyguardManager = getKeyguardManager(context)
Log.d(TAG,
- "notifyCallInvitation pre-requests: device locked ${keyguardManager.isDeviceLocked}, " +
+ "notifyCallInvitation pre-requests: " +
"keyguard locked ${keyguardManager.isKeyguardLocked}, " +
- "callOnLockScreen ${appPreferences.callOnLockScreen.get()}"
+ "callOnLockScreen ${appPreferences.callOnLockScreen.get()}, " +
+ "onForeground ${SimplexApp.context.isAppOnForeground}"
)
+ if (SimplexApp.context.isAppOnForeground) return
val contactId = invitation.contact.id
Log.d(TAG, "notifyCallInvitation $contactId")
val image = invitation.contact.image
@@ -169,15 +172,17 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference
if ((keyguardManager.isKeyguardLocked || screenOff) && appPreferences.callOnLockScreen.get() != CallOnLockScreen.DISABLE) {
val fullScreenIntent = Intent(context, IncomingCallActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
- NotificationCompat.Builder(context, LockScreenCallChannel)
+ NotificationCompat.Builder(context, CallChannel)
.setFullScreenIntent(fullScreenPendingIntent, true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setSilent(false)
} else {
val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + R.raw.ring_once)
+ val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, Intent(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
NotificationCompat.Builder(context, CallChannel)
.setContentIntent(chatPendingIntent(OpenChatAction, invitation.contact.id))
.addAction(R.drawable.ntf_icon, generalGetString(R.string.accept), chatPendingIntent(AcceptCallAction, contactId))
+ .addAction(R.drawable.ntf_icon, generalGetString(R.string.reject), chatPendingIntent(RejectCallAction, contactId, true))
+ .setFullScreenIntent(fullScreenPendingIntent, true)
.setSound(soundUri)
}
val text = generalGetString(
@@ -206,8 +211,11 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference
.setLargeIcon(largeIcon)
.setColor(0x88FFFF)
.setAutoCancel(true)
+ val notification = ntfBuilder.build()
+ // This makes notification sound and vibration repeat endlessly
+ notification.flags = notification.flags or NotificationCompat.FLAG_INSISTENT
with(NotificationManagerCompat.from(context)) {
- notify(CallNotificationId, ntfBuilder.build())
+ notify(CallNotificationId, notification)
}
}
@@ -228,16 +236,20 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference
}
}
- private fun chatPendingIntent(intentAction: String, chatId: String? = null): PendingIntent {
+ private fun chatPendingIntent(intentAction: String, chatId: String? = null, broadcast: Boolean = false): PendingIntent {
Log.d(TAG, "chatPendingIntent for $intentAction")
val uniqueInt = (System.currentTimeMillis() and 0xfffffff).toInt()
- var intent = Intent(context, MainActivity::class.java)
+ var intent = Intent(context, if (!broadcast) MainActivity::class.java else NtfActionReceiver::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
.setAction(intentAction)
if (chatId != null) intent = intent.putExtra(ChatIdKey, chatId)
- return TaskStackBuilder.create(context).run {
- addNextIntentWithParentStack(intent)
- getPendingIntent(uniqueInt, PendingIntent.FLAG_IMMUTABLE)
+ return if (!broadcast) {
+ TaskStackBuilder.create(context).run {
+ addNextIntentWithParentStack(intent)
+ getPendingIntent(uniqueInt, PendingIntent.FLAG_IMMUTABLE)
+ }
+ } else {
+ PendingIntent.getBroadcast(SimplexApp.context, uniqueInt, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
}
}
@@ -255,6 +267,12 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference
acceptContactRequest(cInfo, SimplexApp.context.chatModel)
SimplexApp.context.chatModel.controller.ntfManager.cancelNotificationsForChat(chatId)
}
+ RejectCallAction -> {
+ val invitation = SimplexApp.context.chatModel.callInvitations[chatId]
+ if (invitation != null) {
+ SimplexApp.context.chatModel.callManager.endCall(invitation = invitation)
+ }
+ }
else -> {
Log.e(TAG, "Unknown action. Make sure you provide action from NotificationAction enum")
}
diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt
index 7177fde2f1..043e720b77 100644
--- a/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt
+++ b/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallActivity.kt
@@ -1,5 +1,6 @@
package chat.simplex.app.views.call
+import android.app.Activity
import android.app.KeyguardManager
import android.content.Context
import android.content.Intent
@@ -43,8 +44,7 @@ class IncomingCallActivity: ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val activity = this
- setContent { IncomingCallActivityView(vm.chatModel, activity) }
+ setContent { IncomingCallActivityView(vm.chatModel) }
unlockForIncomingCall()
}
@@ -83,11 +83,12 @@ fun getKeyguardManager(context: Context): KeyguardManager =
context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
@Composable
-fun IncomingCallActivityView(m: ChatModel, activity: IncomingCallActivity) {
+fun IncomingCallActivityView(m: ChatModel) {
val switchingCall = m.switchingCall.value
val invitation = m.activeCallInvitation.value
val call = m.activeCall.value
val showCallView = m.showCallView.value
+ val activity = LocalContext.current as Activity
LaunchedEffect(invitation, call, switchingCall, showCallView) {
if (!switchingCall && invitation == null && (!showCallView || call == null)) {
Log.d(TAG, "IncomingCallActivityView: finishing activity")
@@ -105,20 +106,23 @@ fun IncomingCallActivityView(m: ChatModel, activity: IncomingCallActivity) {
if (invitation != null) IncomingCallAlertView(invitation, m)
}
} else if (invitation != null) {
- IncomingCallLockScreenAlert(invitation, m, activity)
+ IncomingCallLockScreenAlert(invitation, m)
}
}
}
}
@Composable
-fun IncomingCallLockScreenAlert(invitation: RcvCallInvitation, chatModel: ChatModel, activity: IncomingCallActivity) {
+fun IncomingCallLockScreenAlert(invitation: RcvCallInvitation, chatModel: ChatModel) {
val cm = chatModel.callManager
- val cxt = LocalContext.current
- val scope = rememberCoroutineScope()
- var callOnLockScreen by remember { mutableStateOf(chatModel.controller.appPrefs.callOnLockScreen.get()) }
- LaunchedEffect(true) { SoundPlayer.shared.start(cxt, scope, sound = true) }
- DisposableEffect(true) { onDispose { SoundPlayer.shared.stop() } }
+ val callOnLockScreen by remember { mutableStateOf(chatModel.controller.appPrefs.callOnLockScreen.get()) }
+ val context = LocalContext.current
+ DisposableEffect(Unit) {
+ onDispose {
+ // Cancel notification whatever happens next since otherwise sound from notification and from inside the app can co-exist
+ chatModel.controller.ntfManager.cancelCallNotification()
+ }
+ }
IncomingCallLockScreenAlertLayout(
invitation,
callOnLockScreen,
@@ -129,15 +133,14 @@ fun IncomingCallLockScreenAlert(invitation: RcvCallInvitation, chatModel: ChatMo
},
acceptCall = { cm.acceptIncomingCall(invitation = invitation) },
openApp = {
- SoundPlayer.shared.stop()
- var intent = Intent(activity, MainActivity::class.java)
+ val intent = Intent(context, MainActivity::class.java)
.setAction(OpenChatAction)
.putExtra("chatId", invitation.contact.id)
- activity.startActivity(intent)
- activity.finish()
+ context.startActivity(intent)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- getKeyguardManager(activity).requestDismissKeyguard(activity, null)
+ getKeyguardManager(context).requestDismissKeyguard((context as Activity), null)
}
+ (context as Activity).finish()
}
)
}
diff --git a/apps/android/app/src/main/res/values-de/strings.xml b/apps/android/app/src/main/res/values-de/strings.xml
index f38684e53c..a42701dc62 100644
--- a/apps/android/app/src/main/res/values-de/strings.xml
+++ b/apps/android/app/src/main/res/values-de/strings.xml
@@ -115,7 +115,6 @@
SimpleX Chat Nachrichten
SimpleX Chat Anrufe
- SimpleX Chat Anrufe (Sperrbildschirm)
Benachrichtigungsdienst
diff --git a/apps/android/app/src/main/res/values-ru/strings.xml b/apps/android/app/src/main/res/values-ru/strings.xml
index f0a31b831e..4602ee501b 100644
--- a/apps/android/app/src/main/res/values-ru/strings.xml
+++ b/apps/android/app/src/main/res/values-ru/strings.xml
@@ -115,7 +115,6 @@
SimpleX Chat сообщения
SimpleX Chat звонки
- SimpleX Chat звонки (экран блокировки)
Сервис уведомлений
diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml
index e8aeb00267..b5b97611a7 100644
--- a/apps/android/app/src/main/res/values/strings.xml
+++ b/apps/android/app/src/main/res/values/strings.xml
@@ -115,7 +115,6 @@
SimpleX Chat messages
SimpleX Chat calls
- SimpleX Chat calls (lock screen)
Notification service