Reworked & improved how we load contact pictures

This commit is contained in:
Sylvain Berfini 2023-09-28 10:53:59 +02:00
parent a037fb5487
commit e5617d53ee
3 changed files with 32 additions and 91 deletions

View file

@ -25,7 +25,6 @@ import android.content.Context
import android.content.pm.PackageManager
import android.database.Cursor
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.provider.ContactsContract
import androidx.annotation.UiThread
@ -47,6 +46,7 @@ import org.linphone.ui.main.MainActivity
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
@ -304,12 +304,7 @@ class ContactsManager @UiThread constructor(context: Context) {
val personBuilder = Person.Builder().setName(name)
val photo = account?.params?.pictureUri.orEmpty()
val bm: Bitmap? = if (photo.isNotEmpty()) {
BitmapFactory.decodeFile(photo)
} else {
null
}
val bm = ImageUtils.getBitmap(coreContext.context, photo)
personBuilder.setIcon(
if (bm == null) {
coreContext.contactsManager.contactAvatar
@ -329,16 +324,16 @@ class ContactsManager @UiThread constructor(context: Context) {
}
}
@WorkerThread
fun Friend.getAvatarBitmap(): Bitmap? {
return ImageUtils.getBitmap(coreContext.context, photo)
}
@WorkerThread
fun Friend.getPerson(): Person {
val personBuilder = Person.Builder().setName(name)
val bm: Bitmap? = if (!photo.isNullOrEmpty()) {
BitmapFactory.decodeFile(photo)
} else {
null
}
val bm: Bitmap? = getAvatarBitmap()
personBuilder.setIcon(
if (bm == null) {
coreContext.contactsManager.contactAvatar

View file

@ -30,7 +30,6 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ServiceInfo
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import androidx.annotation.AnyThread
import androidx.annotation.MainThread
@ -42,10 +41,10 @@ import androidx.core.app.Person
import androidx.core.app.RemoteInput
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.getAvatarBitmap
import org.linphone.contacts.getPerson
import org.linphone.core.Address
import org.linphone.core.Call
@ -488,12 +487,6 @@ class NotificationsManager @MainThread constructor(private val context: Context)
val contact =
coreContext.contactsManager.findContactByAddress(address)
val contactPicture = contact?.photo
val roundPicture = if (!contactPicture.isNullOrEmpty()) {
BitmapFactory.decodeFile(contactPicture)
} else {
null
}
val displayName = contact?.name ?: LinphoneUtils.getDisplayName(address)
val originalMessage = getTextDescribingMessage(message)
@ -508,7 +501,6 @@ class NotificationsManager @MainThread constructor(private val context: Context)
contact,
displayName,
message.time,
senderAvatar = roundPicture,
isOutgoing = false,
isReaction = true,
reactionToMessageId = message.messageId,
@ -590,12 +582,6 @@ class NotificationsManager @MainThread constructor(private val context: Context)
private fun getNotifiableForChatMessage(message: ChatMessage): NotifiableMessage {
val contact =
coreContext.contactsManager.findContactByAddress(message.fromAddress)
val contactPicture = contact?.photo
val roundPicture = if (!contactPicture.isNullOrEmpty()) {
BitmapFactory.decodeFile(contactPicture)
} else {
null
}
val displayName = contact?.name ?: LinphoneUtils.getDisplayName(message.fromAddress)
val text = getTextDescribingMessage(message)
@ -604,7 +590,6 @@ class NotificationsManager @MainThread constructor(private val context: Context)
contact,
displayName,
message.time * 1000, /* Linphone timestamps are in seconds */
senderAvatar = roundPicture,
isOutgoing = message.isOutgoing
)
@ -648,15 +633,9 @@ class NotificationsManager @MainThread constructor(private val context: Context)
val contact =
coreContext.contactsManager.findContactByAddress(call.remoteAddress)
val contactPicture = contact?.photo
val roundPicture = if (!contactPicture.isNullOrEmpty()) {
BitmapFactory.decodeFile(contactPicture)
} else {
null
}
val displayName = contact?.name ?: LinphoneUtils.getDisplayName(call.remoteAddress)
val person = getPerson(contact, displayName, roundPicture)
val person = getPerson(contact, displayName)
val caller = Person.Builder()
.setName(person.name)
.setIcon(person.icon)
@ -743,12 +722,12 @@ class NotificationsManager @MainThread constructor(private val context: Context)
var lastPerson: Person? = null
for (message in notifiable.messages) {
val friend = message.friend
val person = getPerson(friend, message.sender, message.senderAvatar)
val person = getPerson(friend, message.sender)
if (!message.isOutgoing) {
// We don't want to see our own avatar
lastPerson = person
lastPersonAvatar = message.senderAvatar
lastPersonAvatar = friend?.getAvatarBitmap()
if (allPersons.find { it.key == person.key } == null) {
allPersons.add(person)
@ -797,6 +776,8 @@ class NotificationsManager @MainThread constructor(private val context: Context)
notificationBuilder.addPerson(person)
}
// TODO FIXME: shortcuts!
return notificationBuilder.build()
}
@ -957,17 +938,11 @@ class NotificationsManager @MainThread constructor(private val context: Context)
}
@WorkerThread
private fun getPerson(friend: Friend?, displayName: String, picture: Bitmap?): Person {
private fun getPerson(friend: Friend?, displayName: String): Person {
return friend?.getPerson()
?: Person.Builder()
.setName(displayName)
.setIcon(
if (picture != null) {
IconCompat.createWithAdaptiveBitmap(picture)
} else {
coreContext.contactsManager.contactAvatar
}
)
.setIcon(coreContext.contactsManager.contactAvatar)
.setKey(displayName)
.build()
}
@ -1013,7 +988,6 @@ class NotificationsManager @MainThread constructor(private val context: Context)
channel.enableLights(true)
channel.enableVibration(true)
channel.setShowBadge(true)
channel.setAllowBubbles(false)
notificationManager.createNotificationChannel(channel)
}
@ -1071,7 +1045,6 @@ class NotificationsManager @MainThread constructor(private val context: Context)
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,

View file

@ -21,14 +21,8 @@ package org.linphone.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.ImageDecoder
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.net.Uri
import androidx.annotation.AnyThread
import androidx.annotation.WorkerThread
import java.io.FileNotFoundException
import org.linphone.core.tools.Log
@ -38,58 +32,37 @@ class ImageUtils {
private const val TAG = "[Image Utils]"
@WorkerThread
fun getRoundBitmapFromUri(
fun getBitmap(
context: Context,
fromPictureUri: Uri?
path: String?
): Bitmap? {
var bm: Bitmap? = null
if (fromPictureUri != null) {
bm = try {
Log.d("$TAG Trying to create Bitmap from path [$path]")
if (path != null) {
try {
val fromPictureUri = Uri.parse(path)
if (fromPictureUri == null) {
Log.e("$TAG Failed to parse path [$path] as URI")
return null
}
// We make a copy to ensure Bitmap will be Software and not Hardware, required for shortcuts
ImageDecoder.decodeBitmap(
return ImageDecoder.decodeBitmap(
ImageDecoder.createSource(context.contentResolver, fromPictureUri)
).copy(
Bitmap.Config.ARGB_8888,
true
)
} catch (fnfe: FileNotFoundException) {
Log.e("$TAG File [$path] not found: $fnfe")
return null
} catch (e: Exception) {
Log.e("$TAG Failed to get bitmap from URI [$fromPictureUri]: $e")
Log.e("$TAG Failed to get bitmap using path [$path]: $e")
return null
}
}
if (bm != null) {
val roundBm = getRoundBitmap(bm)
if (roundBm != null) {
bm.recycle()
return roundBm
}
}
return bm
}
@AnyThread
private fun getRoundBitmap(bitmap: Bitmap): Bitmap? {
val output =
Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
val color = -0xbdbdbe
val paint = Paint()
val rect =
Rect(0, 0, bitmap.width, bitmap.height)
paint.isAntiAlias = true
canvas.drawARGB(0, 0, 0, 0)
paint.color = color
canvas.drawCircle(
bitmap.width / 2.toFloat(),
bitmap.height / 2.toFloat(),
bitmap.width / 2.toFloat(),
paint
)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, rect, rect, paint)
return output
Log.e("$TAG Can't get bitmap from null URI")
return null
}
}
}