From 88d9e70ef8a75be71413b102f346dee646cb01ff Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Thu, 26 Jan 2023 21:36:49 +0000 Subject: [PATCH] android: mutliuser-calls (#1845) * android: mutliuser-calls * tint color of icon * userId from function * better line * missing question * bigger avatar Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- .../java/chat/simplex/app/MainActivity.kt | 22 ++++++++--- .../java/chat/simplex/app/model/NtfManager.kt | 39 +++++++++++++------ .../java/chat/simplex/app/model/SimpleXAPI.kt | 6 +-- .../app/views/call/IncomingCallActivity.kt | 6 ++- .../app/views/call/IncomingCallAlertView.kt | 15 +++++-- .../app/views/chatlist/ChatListNavLinkView.kt | 10 ++--- 6 files changed, 69 insertions(+), 29 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index 36a5c3f396..f7ee66fbc4 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -29,6 +29,7 @@ import androidx.fragment.app.FragmentActivity import androidx.lifecycle.* import chat.simplex.app.model.ChatModel import chat.simplex.app.model.NtfManager +import chat.simplex.app.model.NtfManager.Companion.getUserIdFromIntent import chat.simplex.app.ui.theme.SimpleButton import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.SplashView @@ -404,20 +405,31 @@ fun MainPage( } fun processNotificationIntent(intent: Intent?, chatModel: ChatModel) { + val userId = getUserIdFromIntent(intent) when (intent?.action) { NtfManager.OpenChatAction -> { val chatId = intent.getStringExtra("chatId") Log.d(TAG, "processNotificationIntent: OpenChatAction $chatId") if (chatId != null) { - val cInfo = chatModel.getChat(chatId)?.chatInfo - chatModel.clearOverlays.value = true - if (cInfo != null) withApi { openChat(cInfo, chatModel) } + withBGApi { + if (userId != null && userId != chatModel.currentUser.value?.userId) { + chatModel.controller.changeActiveUser(userId) + } + val cInfo = chatModel.getChat(chatId)?.chatInfo + chatModel.clearOverlays.value = true + if (cInfo != null) openChat(cInfo, chatModel) + } } } NtfManager.ShowChatsAction -> { Log.d(TAG, "processNotificationIntent: ShowChatsAction") - chatModel.chatId.value = null - chatModel.clearOverlays.value = true + withBGApi { + if (userId != null && userId != chatModel.currentUser.value?.userId) { + chatModel.controller.changeActiveUser(userId) + } + chatModel.chatId.value = null + chatModel.clearOverlays.value = true + } } NtfManager.AcceptCallAction -> { val chatId = intent.getStringExtra("chatId") 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 7330db3651..fab42b1b3e 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 @@ -30,7 +30,13 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference const val RejectCallAction: String = "chat.simplex.app.REJECT_CALL" const val CallNotificationId: Int = -1 + private const val UserIdKey: String = "userId" private const val ChatIdKey: String = "chatId" + + fun getUserIdFromIntent(intent: Intent?): Long? { + val userId = intent?.getLongExtra(UserIdKey, -1L) + return if (userId == -1L || userId == null) null else userId + } } private val manager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -128,13 +134,14 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference .setColor(0x88FFFF) .setAutoCancel(true) .setVibrate(if (actions.isEmpty()) null else longArrayOf(0, 250, 250, 250)) - .setContentIntent(chatPendingIntent(OpenChatAction, chatId)) + .setContentIntent(chatPendingIntent(OpenChatAction, user.userId, chatId)) .setSilent(if (actions.isEmpty()) recentNotification else false) for (action in actions) { val flags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE val actionIntent = Intent(SimplexApp.context, NtfActionReceiver::class.java) actionIntent.action = action.name + actionIntent.putExtra(UserIdKey, user.userId) actionIntent.putExtra(ChatIdKey, chatId) val actionPendingIntent: PendingIntent = PendingIntent.getBroadcast(SimplexApp.context, 0, actionIntent, flags) val actionButton = when (action) { @@ -149,7 +156,7 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference .setGroup(MessageGroup) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN) .setGroupSummary(true) - .setContentIntent(chatPendingIntent(ShowChatsAction)) + .setContentIntent(chatPendingIntent(ShowChatsAction, user.userId)) .build() with(NotificationManagerCompat.from(context)) { @@ -184,9 +191,9 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference 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)) + .setContentIntent(chatPendingIntent(OpenChatAction, invitation.user.userId, invitation.contact.id)) + .addAction(R.drawable.ntf_icon, generalGetString(R.string.accept), chatPendingIntent(AcceptCallAction, invitation.user.userId, contactId)) + .addAction(R.drawable.ntf_icon, generalGetString(R.string.reject), chatPendingIntent(RejectCallAction, invitation.user.userId, contactId, true)) .setFullScreenIntent(fullScreenPendingIntent, true) .setSound(soundUri) } @@ -243,12 +250,13 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference } } - private fun chatPendingIntent(intentAction: String, chatId: String? = null, broadcast: Boolean = false): PendingIntent { + private fun chatPendingIntent(intentAction: String, userId: Long, chatId: String? = null, broadcast: Boolean = false): PendingIntent { Log.d(TAG, "chatPendingIntent for $intentAction") val uniqueInt = (System.currentTimeMillis() and 0xfffffff).toInt() 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) + .putExtra(UserIdKey, userId) if (chatId != null) intent = intent.putExtra(ChatIdKey, chatId) return if (!broadcast) { TaskStackBuilder.create(context).run { @@ -266,18 +274,25 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference * */ class NtfActionReceiver: BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { + val userId = getUserIdFromIntent(intent) val chatId = intent?.getStringExtra(ChatIdKey) ?: return - val cInfo = SimplexApp.context.chatModel.getChat(chatId)?.chatInfo + val m = SimplexApp.context.chatModel when (intent.action) { NotificationAction.ACCEPT_CONTACT_REQUEST.name -> { - if (cInfo !is ChatInfo.ContactRequest) return - acceptContactRequest(cInfo, SimplexApp.context.chatModel) - SimplexApp.context.chatModel.controller.ntfManager.cancelNotificationsForChat(chatId) + val isCurrentUser = m.currentUser.value?.userId == userId + val cInfo: ChatInfo.ContactRequest? = if (isCurrentUser) { + (m.getChat(chatId)?.chatInfo as? ChatInfo.ContactRequest) ?: return + } else { + null + } + val apiId = chatId.replace("<@", "").toLongOrNull() ?: return + acceptContactRequest(apiId, cInfo, isCurrentUser, m) + m.controller.ntfManager.cancelNotificationsForChat(chatId) } RejectCallAction -> { - val invitation = SimplexApp.context.chatModel.callInvitations[chatId] + val invitation = m.callInvitations[chatId] if (invitation != null) { - SimplexApp.context.chatModel.callManager.endCall(invitation = invitation) + m.callManager.endCall(invitation = invitation) } } else -> { diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index 857a5035e0..603ae95cbe 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -1172,11 +1172,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } } is CR.ReceivedContactRequest -> { - if (!active(r.user)) return - val contactRequest = r.contactRequest val cInfo = ChatInfo.ContactRequest(contactRequest) - chatModel.addChat(Chat(chatInfo = cInfo, chatItems = listOf())) + if (active(r.user)) { + chatModel.addChat(Chat(chatInfo = cInfo, chatItems = listOf())) + } ntfManager.notifyContactRequestReceived(r.user, cInfo) } is CR.ContactUpdated -> { 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 5dd1b71eb4..5877bb6d4d 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 @@ -126,6 +126,7 @@ fun IncomingCallLockScreenAlert(invitation: RcvCallInvitation, chatModel: ChatMo IncomingCallLockScreenAlertLayout( invitation, callOnLockScreen, + chatModel, rejectCall = { cm.endCall(invitation = invitation) }, ignoreCall = { chatModel.activeCallInvitation.value = null @@ -135,6 +136,7 @@ fun IncomingCallLockScreenAlert(invitation: RcvCallInvitation, chatModel: ChatMo openApp = { val intent = Intent(context, MainActivity::class.java) .setAction(OpenChatAction) + .putExtra("userId", invitation.user.userId) .putExtra("chatId", invitation.contact.id) context.startActivity(intent) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -149,6 +151,7 @@ fun IncomingCallLockScreenAlert(invitation: RcvCallInvitation, chatModel: ChatMo fun IncomingCallLockScreenAlertLayout( invitation: RcvCallInvitation, callOnLockScreen: CallOnLockScreen?, + chatModel: ChatModel, rejectCall: () -> Unit, ignoreCall: () -> Unit, acceptCall: () -> Unit, @@ -160,7 +163,7 @@ fun IncomingCallLockScreenAlertLayout( .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally ) { - IncomingCallInfo(invitation) + IncomingCallInfo(invitation, chatModel) Spacer(Modifier.fillMaxHeight().weight(1f)) if (callOnLockScreen == CallOnLockScreen.ACCEPT) { ProfileImage(size = 192.dp, image = invitation.contact.profile.image) @@ -224,6 +227,7 @@ fun PreviewIncomingCallLockScreenAlert() { callTs = Clock.System.now() ), callOnLockScreen = null, + chatModel = SimplexApp.context.chatModel, rejectCall = {}, ignoreCall = {}, acceptCall = {}, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt index b7fce66383..267fd458e7 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/call/IncomingCallAlertView.kt @@ -17,8 +17,10 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import chat.simplex.app.R +import chat.simplex.app.SimplexApp import chat.simplex.app.model.* import chat.simplex.app.ui.theme.* +import chat.simplex.app.views.helpers.ProfileImage import chat.simplex.app.views.usersettings.ProfilePreview import kotlinx.datetime.Clock @@ -31,6 +33,7 @@ fun IncomingCallAlertView(invitation: RcvCallInvitation, chatModel: ChatModel) { DisposableEffect(true) { onDispose { SoundPlayer.shared.stop() } } IncomingCallAlertLayout( invitation, + chatModel, rejectCall = { cm.endCall(invitation = invitation) }, ignoreCall = { chatModel.activeCallInvitation.value = null @@ -43,13 +46,14 @@ fun IncomingCallAlertView(invitation: RcvCallInvitation, chatModel: ChatModel) { @Composable fun IncomingCallAlertLayout( invitation: RcvCallInvitation, + chatModel: ChatModel, rejectCall: () -> Unit, ignoreCall: () -> Unit, acceptCall: () -> Unit ) { val color = if (isInDarkTheme()) IncomingCallDark else IncomingCallLight Column(Modifier.fillMaxWidth().background(color).padding(top = 16.dp, bottom = 16.dp, start = 16.dp, end = 8.dp)) { - IncomingCallInfo(invitation) + IncomingCallInfo(invitation, chatModel) Spacer(Modifier.height(8.dp)) Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween) { Row(Modifier.fillMaxWidth().weight(1f), verticalAlignment = Alignment.CenterVertically) { @@ -65,9 +69,13 @@ fun IncomingCallAlertLayout( } @Composable -fun IncomingCallInfo(invitation: RcvCallInvitation) { +fun IncomingCallInfo(invitation: RcvCallInvitation, chatModel: ChatModel) { @Composable fun CallIcon(icon: ImageVector, descr: String) = Icon(icon, descr, tint = SimplexGreen) - Row { + Row(verticalAlignment = Alignment.CenterVertically) { + if (chatModel.users.size > 1) { + ProfileImage(size = 32.dp, image = invitation.user.profile.image, color = MaterialTheme.colors.secondary) + Spacer(Modifier.width(4.dp)) + } if (invitation.callType.media == CallMediaType.Video) CallIcon(Icons.Filled.Videocam, stringResource(R.string.icon_descr_video_call)) else CallIcon(Icons.Filled.Phone, stringResource(R.string.icon_descr_audio_call)) Spacer(Modifier.width(4.dp)) @@ -106,6 +114,7 @@ fun PreviewIncomingCallAlertLayout() { sharedKey = null, callTs = Clock.System.now() ), + chatModel = SimplexApp.context.chatModel, rejectCall = {}, ignoreCall = {}, acceptCall = {} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt index 0a6fd54463..a5f3cc8afe 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt @@ -301,7 +301,7 @@ fun ContactRequestMenuItems(chatInfo: ChatInfo.ContactRequest, chatModel: ChatMo if (chatModel.incognito.value) Icons.Filled.TheaterComedy else Icons.Outlined.Check, color = if (chatModel.incognito.value) Indigo else MaterialTheme.colors.onBackground, onClick = { - acceptContactRequest(chatInfo, chatModel) + acceptContactRequest(chatInfo.apiId, chatInfo, true, chatModel) showMenu.value = false } ) @@ -409,16 +409,16 @@ fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel title = generalGetString(R.string.accept_connection_request__question), text = generalGetString(R.string.if_you_choose_to_reject_the_sender_will_not_be_notified), confirmText = if (chatModel.incognito.value) generalGetString(R.string.accept_contact_incognito_button) else generalGetString(R.string.accept_contact_button), - onConfirm = { acceptContactRequest(contactRequest, chatModel) }, + onConfirm = { acceptContactRequest(contactRequest.apiId, contactRequest, true, chatModel) }, dismissText = generalGetString(R.string.reject_contact_button), onDismiss = { rejectContactRequest(contactRequest, chatModel) } ) } -fun acceptContactRequest(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel) { +fun acceptContactRequest(apiId: Long, contactRequest: ChatInfo.ContactRequest?, isCurrentUser: Boolean, chatModel: ChatModel) { withApi { - val contact = chatModel.controller.apiAcceptContactRequest(contactRequest.apiId) - if (contact != null) { + val contact = chatModel.controller.apiAcceptContactRequest(apiId) + if (contact != null && isCurrentUser && contactRequest != null) { val chat = Chat(ChatInfo.Direct(contact), listOf()) chatModel.replaceChat(contactRequest.id, chat) }