mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Prevent calls list to scroll to top at each refresh
This commit is contained in:
parent
47c2024f67
commit
d531a47c70
7 changed files with 497 additions and 28 deletions
|
|
@ -41,6 +41,7 @@ import org.linphone.ui.main.contacts.model.ContactNumberOrAddressClickListener
|
|||
import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel
|
||||
import org.linphone.ui.main.model.isInSecureMode
|
||||
import org.linphone.utils.ImageUtils
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
import org.linphone.utils.PhoneNumberUtils
|
||||
|
||||
class ContactsManager @UiThread constructor(context: Context) {
|
||||
|
|
@ -152,6 +153,39 @@ class ContactsManager @UiThread constructor(context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun getMePerson(localAddress: Address): Person {
|
||||
val account = coreContext.core.accountList.find {
|
||||
it.params.identityAddress?.weakEqual(localAddress) ?: false
|
||||
}
|
||||
val name = account?.params?.identityAddress?.displayName ?: LinphoneUtils.getDisplayName(
|
||||
localAddress
|
||||
)
|
||||
val personBuilder = Person.Builder().setName(name)
|
||||
|
||||
val photo = account?.params?.pictureUri.orEmpty()
|
||||
val bm: Bitmap? = if (photo.isNotEmpty()) {
|
||||
ImageUtils.getRoundBitmapFromUri(
|
||||
coreContext.context,
|
||||
Uri.parse(photo ?: "")
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
personBuilder.setIcon(
|
||||
if (bm == null) {
|
||||
coreContext.contactsManager.contactAvatar
|
||||
} else {
|
||||
IconCompat.createWithAdaptiveBitmap(bm)
|
||||
}
|
||||
)
|
||||
|
||||
personBuilder.setKey("") // TODO FIXME: use a valid key
|
||||
personBuilder.setImportant(false)
|
||||
return personBuilder.build()
|
||||
}
|
||||
|
||||
interface ContactsListener {
|
||||
fun onContactsLoaded()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,6 +193,14 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C
|
|||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun isAddressMyself(address: Address): Boolean {
|
||||
val found = core.accountList.find {
|
||||
it.params.identityAddress?.weakEqual(address) ?: false
|
||||
}
|
||||
return found != null
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun startCall(
|
||||
address: Address,
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class CorePreferences @UiThread constructor(private val context: Context) {
|
|||
// Will disable chat feature completely
|
||||
@get:WorkerThread
|
||||
val disableChat: Boolean
|
||||
get() = config.getBool("app", "disable_chat_feature", false)
|
||||
get() = config.getBool("app", "disable_chat_feature", false) // TODO FIXME: set it to true for first "release" without chat
|
||||
|
||||
@get:WorkerThread
|
||||
val defaultDomain: String
|
||||
|
|
|
|||
|
|
@ -33,24 +33,30 @@ import android.graphics.Bitmap
|
|||
import android.net.Uri
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.app.Person
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.LocusIdCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.LinphoneApplication.Companion.corePreferences
|
||||
import org.linphone.R
|
||||
import org.linphone.contacts.getPerson
|
||||
import org.linphone.core.Address
|
||||
import org.linphone.core.Call
|
||||
import org.linphone.core.ChatMessage
|
||||
import org.linphone.core.ChatMessageReaction
|
||||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.core.Core
|
||||
import org.linphone.core.CoreForegroundService
|
||||
import org.linphone.core.CoreListenerStub
|
||||
import org.linphone.core.Friend
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.voip.VoipActivity
|
||||
import org.linphone.utils.AppUtils
|
||||
import org.linphone.utils.ImageUtils
|
||||
import org.linphone.utils.LinphoneUtils
|
||||
|
||||
|
|
@ -63,6 +69,9 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
|
||||
const val INTENT_CALL_ID = "CALL_ID"
|
||||
const val INTENT_NOTIF_ID = "NOTIFICATION_ID"
|
||||
|
||||
const val CHAT_TAG = "Chat"
|
||||
const val CHAT_NOTIFICATIONS_GROUP = "CHAT_NOTIF_GROUP"
|
||||
}
|
||||
|
||||
private val notificationManager: NotificationManagerCompat by lazy {
|
||||
|
|
@ -97,16 +106,122 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
Log.i("$TAG Last call ended, stopping foreground service")
|
||||
stopCallForeground()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onMessagesReceived(
|
||||
core: Core,
|
||||
chatRoom: ChatRoom,
|
||||
messages: Array<out ChatMessage>
|
||||
) {
|
||||
Log.i("$TAG Received ${messages.size} aggregated messages")
|
||||
if (corePreferences.disableChat) return
|
||||
|
||||
if (chatRoom.muted) {
|
||||
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
||||
Log.i("$TAG Chat room $id has been muted")
|
||||
return
|
||||
}
|
||||
|
||||
showChatRoomNotification(chatRoom, messages)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onNewMessageReaction(
|
||||
core: Core,
|
||||
chatRoom: ChatRoom,
|
||||
message: ChatMessage,
|
||||
reaction: ChatMessageReaction
|
||||
) {
|
||||
val address = reaction.fromAddress
|
||||
val defaultAccountAddress = core.defaultAccount?.params?.identityAddress
|
||||
// Do not notify our own reactions, it won't be done anyway since the chat room is very likely to be currently displayed
|
||||
if (defaultAccountAddress != null && defaultAccountAddress.weakEqual(address)) return
|
||||
|
||||
Log.i(
|
||||
"$TAG Reaction received [${reaction.body}] from [${address.asStringUriOnly()}] for chat message [$message]"
|
||||
)
|
||||
if (corePreferences.disableChat) return
|
||||
|
||||
if (chatRoom.muted) {
|
||||
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
||||
Log.i("$TAG Chat room $id has been muted")
|
||||
return
|
||||
}
|
||||
if (coreContext.isAddressMyself(address)) {
|
||||
Log.i("$TAG Reaction has been sent by ourselves, do not notify it")
|
||||
return
|
||||
}
|
||||
|
||||
if (reaction.body.isNotEmpty()) {
|
||||
showChatMessageReactionNotification(chatRoom, reaction.body, address, message)
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onReactionRemoved(
|
||||
core: Core,
|
||||
chatRoom: ChatRoom,
|
||||
message: ChatMessage,
|
||||
address: Address
|
||||
) {
|
||||
Log.i(
|
||||
"$TAG [${address.asStringUriOnly()}] removed it's previously sent reaction for chat message [$message]"
|
||||
)
|
||||
if (corePreferences.disableChat) return
|
||||
|
||||
if (chatRoom.muted) {
|
||||
val id = LinphoneUtils.getChatRoomId(chatRoom.localAddress, chatRoom.peerAddress)
|
||||
Log.i("$TAG Chat room $id has been muted")
|
||||
return
|
||||
}
|
||||
|
||||
val chatRoomPeerAddress = chatRoom.peerAddress.asStringUriOnly()
|
||||
var notifiable: Notifiable? = chatNotificationsMap[chatRoomPeerAddress]
|
||||
if (notifiable == null) {
|
||||
Log.i("$TAG No notification for chat room [$chatRoomPeerAddress], nothing to do")
|
||||
return
|
||||
}
|
||||
|
||||
val from = address.asStringUriOnly()
|
||||
val found = notifiable.messages.find {
|
||||
it.isReaction && it.reactionToMessageId == message.messageId && it.reactionFrom == from
|
||||
}
|
||||
if (found != null) {
|
||||
if (notifiable.messages.remove(found)) {
|
||||
if (notifiable.messages.isNotEmpty()) {
|
||||
Log.i(
|
||||
"$TAG After removing original reaction notification there is still messages, updating notification"
|
||||
)
|
||||
val me = coreContext.contactsManager.getMePerson(chatRoom.localAddress)
|
||||
val notification = createMessageNotification(
|
||||
notifiable,
|
||||
LinphoneUtils.getChatRoomId(chatRoom),
|
||||
me
|
||||
)
|
||||
notify(notifiable.notificationId, notification, CHAT_TAG)
|
||||
} else {
|
||||
Log.i(
|
||||
"$TAG After removing original reaction notification there is nothing left to display, remove notification"
|
||||
)
|
||||
notificationManager.cancel(CHAT_TAG, notifiable.notificationId)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w("$TAG Original reaction not found in currently displayed notification")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var coreService: CoreForegroundService? = null
|
||||
|
||||
private val callNotificationsMap: HashMap<String, Notifiable> = HashMap()
|
||||
private val chatNotificationsMap: HashMap<String, Notifiable> = HashMap()
|
||||
|
||||
init {
|
||||
createServiceChannel()
|
||||
createIncomingCallNotificationChannel()
|
||||
createActiveCallNotificationChannel()
|
||||
createMessageChannel()
|
||||
|
||||
for (notification in notificationManager.activeNotifications) {
|
||||
if (notification.tag.isNullOrEmpty()) {
|
||||
|
|
@ -171,13 +286,7 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
pendingIntent,
|
||||
isIncoming
|
||||
)
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
notify(notifiable.notificationId, notification)
|
||||
}
|
||||
notify(notifiable.notificationId, notification)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
|
@ -230,23 +339,141 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
|
||||
private fun getNotifiableForRoom(chatRoom: ChatRoom): Notifiable {
|
||||
val address = chatRoom.peerAddress.asStringUriOnly()
|
||||
var notifiable: Notifiable? = chatNotificationsMap[address]
|
||||
if (notifiable == null) {
|
||||
notifiable = Notifiable(LinphoneUtils.getChatRoomId(chatRoom).hashCode())
|
||||
notifiable.myself = LinphoneUtils.getDisplayName(chatRoom.localAddress)
|
||||
notifiable.localIdentity = chatRoom.localAddress.asStringUriOnly()
|
||||
notifiable.remoteAddress = chatRoom.peerAddress.asStringUriOnly()
|
||||
|
||||
chatNotificationsMap[address] = notifiable
|
||||
|
||||
if (chatRoom.hasCapability(ChatRoom.Capabilities.OneToOne.toInt())) {
|
||||
notifiable.isGroup = false
|
||||
} else {
|
||||
notifiable.isGroup = true
|
||||
notifiable.groupTitle = chatRoom.subject
|
||||
}
|
||||
}
|
||||
return notifiable
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun showChatRoomNotification(chatRoom: ChatRoom, messages: Array<out ChatMessage>) {
|
||||
val notifiable = getNotifiableForRoom(chatRoom)
|
||||
|
||||
var updated = false
|
||||
for (message in messages) {
|
||||
if (message.isRead || message.isOutgoing) continue
|
||||
val notifiableMessage = getNotifiableForChatMessage(message)
|
||||
notifiable.messages.add(notifiableMessage)
|
||||
updated = true
|
||||
}
|
||||
|
||||
if (chatRoom.hasCapability(ChatRoom.Capabilities.OneToOne.toInt())) {
|
||||
notifiable.isGroup = false
|
||||
} else {
|
||||
notifiable.isGroup = true
|
||||
notifiable.groupTitle = chatRoom.subject
|
||||
}
|
||||
if (!updated) {
|
||||
Log.w("$TAG No changes made to notifiable, do not display it again")
|
||||
return
|
||||
}
|
||||
|
||||
if (notifiable.messages.isNotEmpty()) {
|
||||
val me = coreContext.contactsManager.getMePerson(chatRoom.localAddress)
|
||||
val notification = createMessageNotification(
|
||||
notifiable,
|
||||
LinphoneUtils.getChatRoomId(chatRoom),
|
||||
me
|
||||
)
|
||||
notify(notifiable.notificationId, notification, CHAT_TAG)
|
||||
} else {
|
||||
Log.w(
|
||||
"$TAG No message to display in received aggregated messages"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun showChatMessageReactionNotification(
|
||||
chatRoom: ChatRoom,
|
||||
reaction: String,
|
||||
from: Address,
|
||||
message: ChatMessage
|
||||
) {
|
||||
val notifiable = getNotifiableForRoom(chatRoom)
|
||||
|
||||
val contact =
|
||||
coreContext.contactsManager.findContactByAddress(from)
|
||||
val contactPicture = contact?.photo
|
||||
val roundPicture = if (!contactPicture.isNullOrEmpty()) {
|
||||
ImageUtils.getRoundBitmapFromUri(context, Uri.parse(contactPicture))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val displayName = contact?.name ?: LinphoneUtils.getDisplayName(from)
|
||||
|
||||
val originalMessage = getTextDescribingMessage(message)
|
||||
val text = AppUtils.getString(R.string.chat_message_reaction_received).format(
|
||||
displayName,
|
||||
reaction,
|
||||
originalMessage
|
||||
)
|
||||
|
||||
val notifiableMessage = NotifiableMessage(
|
||||
text,
|
||||
contact,
|
||||
displayName,
|
||||
message.time,
|
||||
senderAvatar = roundPicture,
|
||||
isOutgoing = false,
|
||||
isReaction = true,
|
||||
reactionToMessageId = message.messageId,
|
||||
reactionFrom = from.asStringUriOnly()
|
||||
)
|
||||
notifiable.messages.add(notifiableMessage)
|
||||
|
||||
if (notifiable.messages.isNotEmpty()) {
|
||||
val me = coreContext.contactsManager.getMePerson(chatRoom.localAddress)
|
||||
val notification = createMessageNotification(
|
||||
notifiable,
|
||||
LinphoneUtils.getChatRoomId(chatRoom),
|
||||
me
|
||||
)
|
||||
notify(notifiable.notificationId, notification, CHAT_TAG)
|
||||
} else {
|
||||
Log.e(
|
||||
"$TAG Notifiable is empty but we should have displayed the reaction!"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun notify(id: Int, notification: Notification, tag: String? = null) {
|
||||
Log.i("$TAG Notifying [$id] with tag [$tag]")
|
||||
try {
|
||||
notificationManager.notify(tag, id, notification)
|
||||
} catch (iae: IllegalArgumentException) {
|
||||
if (coreService == null && tag == null) {
|
||||
// We can't notify using CallStyle if there isn't a foreground service running
|
||||
Log.w(
|
||||
"$TAG Foreground service hasn't started yet, can't display a CallStyle notification until then: $iae"
|
||||
)
|
||||
} else {
|
||||
Log.e("$TAG Illegal Argument Exception occurred: $iae")
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
Log.i("$TAG Notifying [$id] with tag [$tag]")
|
||||
try {
|
||||
notificationManager.notify(tag, id, notification)
|
||||
} catch (iae: IllegalArgumentException) {
|
||||
if (coreService == null && tag == null) {
|
||||
// We can't notify using CallStyle if there isn't a foreground service running
|
||||
Log.w(
|
||||
"$TAG Foreground service hasn't started yet, can't display a CallStyle notification until then: $iae"
|
||||
)
|
||||
} else {
|
||||
Log.e("$TAG Illegal Argument Exception occurred: $iae")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("$TAG Exception occurred: $e")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("$TAG Exception occurred: $e")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -274,6 +501,55 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
return notifiable
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun getNotifiableForChatMessage(message: ChatMessage): NotifiableMessage {
|
||||
val contact =
|
||||
coreContext.contactsManager.findContactByAddress(message.fromAddress)
|
||||
val contactPicture = contact?.photo
|
||||
val roundPicture = if (!contactPicture.isNullOrEmpty()) {
|
||||
ImageUtils.getRoundBitmapFromUri(context, Uri.parse(contactPicture))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val displayName = contact?.name ?: LinphoneUtils.getDisplayName(message.fromAddress)
|
||||
|
||||
val text = getTextDescribingMessage(message)
|
||||
val notifiableMessage = NotifiableMessage(
|
||||
text,
|
||||
contact,
|
||||
displayName,
|
||||
message.time * 1000, /* Linphone timestamps are in seconds */
|
||||
senderAvatar = roundPicture,
|
||||
isOutgoing = message.isOutgoing
|
||||
)
|
||||
|
||||
for (content in message.contents) {
|
||||
/*if (content.isFile) { // TODO
|
||||
val path = content.filePath
|
||||
if (path != null) {
|
||||
val contentUri: Uri = FileUtils.getFilePath(context, path)
|
||||
val filePath: String = contentUri.toString()
|
||||
val extension = FileUtils.getExtensionFromFileName(filePath)
|
||||
if (extension.isNotEmpty()) {
|
||||
val mime =
|
||||
MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
|
||||
notifiableMessage.filePath = contentUri
|
||||
notifiableMessage.fileMime = mime
|
||||
Log.i(
|
||||
"$TAG Added file $contentUri with MIME $mime to notification"
|
||||
)
|
||||
} else {
|
||||
Log.e(
|
||||
"$TAG Couldn't find extension for incoming message with file $path"
|
||||
)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
return notifiableMessage
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun createCallNotification(
|
||||
context: Context,
|
||||
|
|
@ -370,6 +646,73 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
return builder.build()
|
||||
}
|
||||
|
||||
private fun createMessageNotification(
|
||||
notifiable: Notifiable,
|
||||
id: String,
|
||||
me: Person
|
||||
): Notification {
|
||||
val style = NotificationCompat.MessagingStyle(me)
|
||||
val allPersons = arrayListOf<Person>()
|
||||
|
||||
var lastPersonAvatar: Bitmap? = null
|
||||
var lastPerson: Person? = null
|
||||
for (message in notifiable.messages) {
|
||||
val friend = message.friend
|
||||
val person = getPerson(friend, message.sender, message.senderAvatar)
|
||||
|
||||
if (!message.isOutgoing) {
|
||||
// We don't want to see our own avatar
|
||||
lastPerson = person
|
||||
lastPersonAvatar = message.senderAvatar
|
||||
|
||||
if (allPersons.find { it.key == person.key } == null) {
|
||||
allPersons.add(person)
|
||||
}
|
||||
}
|
||||
|
||||
val senderPerson = if (message.isOutgoing) null else person // Use null for ourselves
|
||||
val tmp = NotificationCompat.MessagingStyle.Message(
|
||||
message.message,
|
||||
message.time,
|
||||
senderPerson
|
||||
)
|
||||
if (message.filePath != null) tmp.setData(message.fileMime, message.filePath)
|
||||
|
||||
style.addMessage(tmp)
|
||||
if (message.isOutgoing) {
|
||||
style.addHistoricMessage(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
style.conversationTitle = if (notifiable.isGroup) notifiable.groupTitle else lastPerson?.name
|
||||
style.isGroupConversation = notifiable.isGroup
|
||||
|
||||
val largeIcon = lastPersonAvatar
|
||||
val notificationBuilder = NotificationCompat.Builder(
|
||||
context,
|
||||
context.getString(R.string.notification_channel_chat_id)
|
||||
)
|
||||
.setSmallIcon(R.drawable.chat_text)
|
||||
.setAutoCancel(true)
|
||||
.setLargeIcon(largeIcon)
|
||||
.setColor(ContextCompat.getColor(context, R.color.primary_color))
|
||||
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
.setGroup(CHAT_NOTIFICATIONS_GROUP)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
|
||||
.setNumber(notifiable.messages.size)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setShowWhen(true)
|
||||
.setStyle(style)
|
||||
.setShortcutId(id)
|
||||
.setLocusId(LocusIdCompat(id))
|
||||
|
||||
for (person in allPersons) {
|
||||
notificationBuilder.addPerson(person)
|
||||
}
|
||||
|
||||
return notificationBuilder.build()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun dismissCallNotification(call: Call) {
|
||||
val address = call.remoteAddress.asStringUriOnly()
|
||||
|
|
@ -457,6 +800,22 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private fun createMessageChannel() {
|
||||
val id = context.getString(R.string.notification_channel_chat_id)
|
||||
val name = context.getString(R.string.notification_channel_chat_name)
|
||||
|
||||
val channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
|
||||
channel.description = name
|
||||
channel.lightColor = context.getColor(R.color.primary_color)
|
||||
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
channel.enableLights(true)
|
||||
channel.enableVibration(true)
|
||||
channel.setShowBadge(true)
|
||||
channel.setAllowBubbles(true)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private fun createServiceChannel() {
|
||||
val id = context.getString(R.string.notification_channel_service_id)
|
||||
|
|
@ -470,8 +829,53 @@ class NotificationsManager @MainThread constructor(private val context: Context)
|
|||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun getTextDescribingMessage(message: ChatMessage): String {
|
||||
// If message contains text, then use that
|
||||
var text = message.contents.find { content -> content.isText }?.utf8Text ?: ""
|
||||
|
||||
if (text.isEmpty()) {
|
||||
val firstContent = message.contents.firstOrNull()
|
||||
if (firstContent?.isIcalendar == true) {
|
||||
text = "meeting invite" // TODO: use translated string
|
||||
} else if (firstContent?.isVoiceRecording == true) {
|
||||
text = "voice message" // TODO: use translated string
|
||||
} else {
|
||||
for (content in message.contents) {
|
||||
if (text.isNotEmpty()) {
|
||||
text += ", "
|
||||
}
|
||||
text += content.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
class Notifiable(val notificationId: Int) {
|
||||
var myself: String? = null
|
||||
var callId: String? = null
|
||||
|
||||
var localIdentity: String? = null
|
||||
var remoteAddress: String? = null
|
||||
|
||||
var isGroup: Boolean = false
|
||||
var groupTitle: String? = null
|
||||
val messages: ArrayList<NotifiableMessage> = arrayListOf()
|
||||
}
|
||||
|
||||
class NotifiableMessage(
|
||||
var message: String,
|
||||
val friend: Friend?,
|
||||
val sender: String,
|
||||
val time: Long,
|
||||
val senderAvatar: Bitmap? = null,
|
||||
var filePath: Uri? = null,
|
||||
var fileMime: String? = null,
|
||||
val isOutgoing: Boolean = false,
|
||||
val isReaction: Boolean = false,
|
||||
val reactionToMessageId: String? = null,
|
||||
val reactionFrom: String? = null
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,13 +148,17 @@ class CallsListFragment : AbstractTopBarFragment() {
|
|||
binding.callsList.layoutManager = layoutManager
|
||||
|
||||
listViewModel.callLogs.observe(viewLifecycleOwner) {
|
||||
Log.i("$TAG Call logs ready with [${it.size}] items")
|
||||
val currentCount = adapter.itemCount
|
||||
adapter.submitList(it)
|
||||
binding.callsList.scrollToPosition(0)
|
||||
Log.i("$TAG Call logs ready with [${it.size}] items")
|
||||
|
||||
(view.parent as? ViewGroup)?.doOnPreDraw {
|
||||
startPostponedEnterTransition()
|
||||
sharedViewModel.callsListReadyToBeDisplayedEvent.value = Event(true)
|
||||
if (currentCount == 0) {
|
||||
(view.parent as? ViewGroup)?.doOnPreDraw {
|
||||
startPostponedEnterTransition()
|
||||
sharedViewModel.callsListReadyToBeDisplayedEvent.value = Event(true)
|
||||
}
|
||||
} else if (currentCount < it.size) {
|
||||
binding.callsList.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import org.linphone.core.Address
|
|||
import org.linphone.core.Call
|
||||
import org.linphone.core.Call.Dir
|
||||
import org.linphone.core.Call.Status
|
||||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.core.tools.Log
|
||||
|
||||
class LinphoneUtils {
|
||||
|
|
@ -174,5 +175,19 @@ class LinphoneUtils {
|
|||
}
|
||||
return name
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun getChatRoomId(room: ChatRoom): String {
|
||||
return getChatRoomId(room.localAddress, room.peerAddress)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun getChatRoomId(localAddress: Address, remoteAddress: Address): String {
|
||||
val localSipUri = localAddress.clone()
|
||||
localSipUri.clean()
|
||||
val remoteSipUri = remoteAddress.clone()
|
||||
remoteSipUri.clean()
|
||||
return "${localSipUri.asStringUriOnly()}~${remoteSipUri.asStringUriOnly()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
<string name="notification_channel_call_id" translatable="false">linphone_notification_call_id</string>
|
||||
<string name="notification_channel_incoming_call_id" translatable="false">linphone_notification_incoming_call_id</string>
|
||||
<string name="notification_channel_service_id" translatable="false">linphone_notification_service_id</string>
|
||||
<string name="notification_channel_chat_id" translatable="false">linphone_notification_chat_id</string>
|
||||
|
||||
<string name="help_about_open_source_licenses_title" translatable="false">GNU General Public License v3.0</string>
|
||||
<string name="help_about_open_source_licenses_subtitle" translatable="false">© Belledonne Communications 2010-2023</string>
|
||||
|
|
@ -40,6 +41,7 @@
|
|||
<string name="notification_channel_call_name">&appName; active calls notifications</string>
|
||||
<string name="notification_channel_incoming_call_name">&appName; incoming calls notifications</string>
|
||||
<string name="notification_channel_service_name">&appName; service notification</string>
|
||||
<string name="notification_channel_chat_name">&appName; instant messages notifications</string>
|
||||
|
||||
<string name="bottom_navigation_contacts_label">Contacts</string>
|
||||
<string name="bottom_navigation_calls_label">Calls</string>
|
||||
|
|
@ -252,6 +254,8 @@
|
|||
<string name="voip_action_pause_call">Pause</string>
|
||||
<string name="voip_action_resume_call">Pause</string>
|
||||
<string name="voip_action_record_call">Record</string>
|
||||
|
||||
<string name="chat_message_reaction_received">%s has reacted by %s to: %s</string>
|
||||
|
||||
<!-- Keep <u></u> in following strings translations! -->
|
||||
<string name="welcome_carousel_skip"><u>Skip</u></string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue