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>
This commit is contained in:
Stanislav Dmitrenko
2023-01-26 21:36:49 +00:00
committed by GitHub
parent a0cfc19063
commit 88d9e70ef8
6 changed files with 69 additions and 29 deletions

View File

@@ -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")

View File

@@ -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 -> {

View File

@@ -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 -> {

View File

@@ -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 = {},

View File

@@ -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 = {}

View File

@@ -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)
}