Removed avatar generation for conversations/meetings depending on members avatars

This commit is contained in:
Sylvain Berfini 2024-04-25 12:09:52 +02:00
parent 81af7a8bc0
commit d848622ace
12 changed files with 19 additions and 231 deletions

View file

@ -30,7 +30,7 @@ abstract class AbstractAvatarModel {
val initials = MutableLiveData<String>()
val images = MutableLiveData<ArrayList<String>>()
val picturePath = MutableLiveData<String>()
val forceConversationIcon = MutableLiveData<Boolean>()

View file

@ -35,7 +35,6 @@ import org.linphone.core.Friend
import org.linphone.core.tools.Log
import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.utils.AppUtils
import org.linphone.utils.ImageUtils
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.ShortcutUtils
import org.linphone.utils.TimestampUtils
@ -335,7 +334,6 @@ class ConversationModel @WorkerThread constructor(
if (isGroup) {
val fakeFriend = coreContext.core.createFriend()
fakeFriend.name = chatRoom.subject
fakeFriend.photo = ImageUtils.generateBitmapForChatRoom(chatRoom)
val model = ContactAvatarModel(fakeFriend)
model.defaultToConversationIcon.postValue(true)
avatarModel.postValue(model)

View file

@ -41,7 +41,6 @@ import org.linphone.ui.main.chat.model.ParticipantModel
import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.ImageUtils
import org.linphone.utils.LinphoneUtils
class ConversationInfoViewModel @UiThread constructor() : AbstractConversationViewModel() {
@ -529,7 +528,6 @@ class ConversationInfoViewModel @UiThread constructor() : AbstractConversationVi
val avatar = if (groupChatRoom) {
val fakeFriend = coreContext.core.createFriend()
fakeFriend.name = chatRoom.subject
fakeFriend.photo = ImageUtils.generateBitmapForChatRoom(chatRoom)
val model = ContactAvatarModel(fakeFriend)
model.defaultToConversationIcon.postValue(true)
model

View file

@ -43,7 +43,6 @@ import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.ui.main.model.isEndToEndEncryptionMandatory
import org.linphone.utils.AppUtils
import org.linphone.utils.Event
import org.linphone.utils.ImageUtils
import org.linphone.utils.LinphoneUtils
class ConversationViewModel @UiThread constructor() : AbstractConversationViewModel() {
@ -538,7 +537,6 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo
val avatar = if (group) {
val fakeFriend = coreContext.core.createFriend()
fakeFriend.name = chatRoom.subject
fakeFriend.photo = ImageUtils.generateBitmapForChatRoom(chatRoom)
val model = ContactAvatarModel(fakeFriend)
model
} else {

View file

@ -88,7 +88,7 @@ class ContactAvatarModel @WorkerThread constructor(val friend: Friend, val addre
isFavourite.postValue(friend.starred)
initials.postValue(AppUtils.getInitials(friend.name.orEmpty()))
showTrust.postValue(coreContext.core.defaultAccount?.isEndToEndEncryptionMandatory())
images.postValue(arrayListOf(getAvatarUri(friend).toString()))
picturePath.postValue(getAvatarUri(friend).toString())
name.postValue(friend.name)
computePresence(address)

View file

@ -172,8 +172,8 @@ class AccountModel @WorkerThread constructor(
initials.postValue(AppUtils.getInitials(name))
val pictureUri = account.params.pictureUri.orEmpty()
if (pictureUri != images.value?.firstOrNull()) {
images.postValue(arrayListOf(pictureUri))
if (pictureUri != picturePath.value.orEmpty()) {
picturePath.postValue(pictureUri)
Log.d("$TAG Account picture URI is [$pictureUri]")
}

View file

@ -195,7 +195,7 @@ class AccountProfileViewModel @UiThread constructor() : ViewModel() {
// Create a new model to force UI to update
val newModel = AccountModel(account)
newModel.images.postValue(arrayListOf(path))
newModel.picturePath.postValue(path)
accountModel.postValue(newModel)
account.params = copy

View file

@ -393,37 +393,18 @@ private fun loadContactPictureWithCoil(
return
}
val images = model.images.value.orEmpty()
val count = images.size
if (count == 1) {
val image = images.firstOrNull()
if (image != null) {
imageView.load(image) {
transformations(CircleCropTransformation())
listener(
onError = { _, _ ->
imageView.load(getErrorImageLoader(context, model, size, textSize))
}
)
}
} else {
imageView.load(getErrorImageLoader(context, model, size, textSize))
val image = model.picturePath.value.orEmpty()
if (image != null) {
imageView.load(image) {
transformations(CircleCropTransformation())
listener(
onError = { _, _ ->
imageView.load(getErrorImageLoader(context, model, size, textSize))
}
)
}
} else {
val w = if (size > 0) {
AppUtils.getDimension(size).toInt()
} else {
AppUtils.getDimension(R.dimen.avatar_list_cell_size).toInt()
}
val bitmap = ImageUtils.getBitmapFromMultipleAvatars(imageView.context, w, images)
if (bitmap != null) {
imageView.load(bitmap) {
transformations(CircleCropTransformation())
}
} else {
val initials = model.initials.value.orEmpty()
imageView.load(ImageUtils.getGeneratedAvatar(context, size, textSize, initials))
}
imageView.load(getErrorImageLoader(context, model, size, textSize))
}
}
}

View file

@ -21,21 +21,13 @@ package org.linphone.utils
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.ImageDecoder
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.net.Uri
import androidx.annotation.AnyThread
import androidx.annotation.WorkerThread
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.OutputStream
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.contacts.AvatarGenerator
import org.linphone.core.ChatRoom
import org.linphone.core.tools.Log
class ImageUtils {
@ -90,129 +82,5 @@ class ImageUtils {
Log.e("$TAG Can't get bitmap from null URI")
return null
}
@WorkerThread
fun generateBitmapForChatRoom(chatRoom: ChatRoom): String {
val id = LinphoneUtils.getChatRoomId(chatRoom)
val hash = id.hashCode().toString()
val file = FileUtils.getFileStorageCacheDir("$hash.jpg", overrideExisting = true)
if (file.exists()) {
Log.i("$TAG Bitmap for conversation [$id]($hash) exists, using it")
return FileUtils.getProperFilePath(file.absolutePath)
}
val list = arrayListOf<String>()
for (participant in chatRoom.participants) {
val contact =
coreContext.contactsManager.findContactByAddress(participant.address)
val picture = contact?.photo
if (picture != null) {
list.add(picture)
}
}
if (list.isNotEmpty() && coreContext.contactsManager.areContactsAvailable()) {
Log.i(
"$TAG Found at [${list.size}] participant(s) with a picture for conversation [$id]($hash), creating avatar"
)
val bitmap = generateBitmapFromList(list)
if (bitmap == null) {
Log.e("$TAG Avatar couldn't be generated")
return ""
}
val outputStream: OutputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
outputStream.close()
val generatedPath = FileUtils.getProperFilePath(file.absolutePath)
Log.i("$TAG Avatar for conversation [$id]($hash) was generated at [$generatedPath]")
return generatedPath
} else {
Log.w(
"$TAG Not once picture found for conversation [$id], couldn't generate avatar"
)
}
return ""
}
@WorkerThread
fun generateBitmapFromList(list: ArrayList<String>): Bitmap? {
val size = AppUtils.getDimension(R.dimen.avatar_in_call_size).toInt()
return getBitmapFromMultipleAvatars(coreContext.context, size, list)
}
@AnyThread
fun getBitmapFromMultipleAvatars(context: Context, size: Int, images: List<String>): Bitmap? {
val drawables = images.mapNotNull {
try {
val uri = Uri.parse(it)
val stream = context.contentResolver.openInputStream(uri)
val bm = BitmapFactory.decodeStream(stream)
if (bm != null) {
Bitmap.createScaledBitmap(bm, size, size, false)
} else {
null
}
} catch (e: Exception) {
Log.e("$TAG Failed to get scaled bitmap for URI [$it]")
null
}
}
if (drawables.isEmpty()) {
Log.e("$TAG Drawables list is empty, can't generate bitmap without at least one")
return null
} else {
Log.i("$TAG Generating avatar using [${drawables.size}] drawables")
}
val rectangles = if (drawables.size == 2) {
arrayListOf(
Rect(0, 0, size / 2, size),
Rect(size / 2, 0, size, size)
)
} else if (drawables.size == 3) {
arrayListOf(
Rect(0, 0, size / 2, size / 2),
Rect(size / 2, 0, size, size / 2),
Rect(0, size / 2, size, size)
)
} else if (drawables.size >= 4) {
arrayListOf(
Rect(0, 0, size / 2, size / 2),
Rect(size / 2, 0, size, size / 2),
Rect(0, size / 2, size / 2, size),
Rect(size / 2, size / 2, size, size)
)
} else {
arrayListOf(Rect(0, 0, size, size))
}
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
for (i in 0 until rectangles.size) {
val src = if (drawables.size == 3 && i == 2) {
// To prevent deformation for the bottom image when merging 3 of them
val quarter = size / 4
Rect(0, quarter, size, 3 * quarter)
} else if (drawables.size == 2) {
// To prevent deformation when two images are next to each other
val quarter = size / 4
Rect(quarter, 0, 3 * quarter, size)
} else {
Rect(0, 0, size, size)
}
try {
canvas.drawBitmap(
drawables[i]/*.toBitmap(size, size, Bitmap.Config.ARGB_8888)*/,
src,
rectangles[i],
null
)
} catch (_: Exception) {}
}
return bitmap
}
}
}

View file

@ -19,13 +19,10 @@
*/
package org.linphone.utils
import android.graphics.Bitmap
import androidx.annotation.AnyThread
import androidx.annotation.DrawableRes
import androidx.annotation.IntegerRes
import androidx.annotation.WorkerThread
import java.io.FileOutputStream
import java.io.OutputStream
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
@ -415,46 +412,6 @@ class LinphoneUtils {
fakeFriend.address = conferenceInfo.uri
fakeFriend.name = conferenceInfo.subject
var avatarFound = true
val hash = conferenceInfo.uri?.asStringUriOnly().hashCode().toString()
val file = FileUtils.getFileStorageCacheDir("$hash.jpg", overrideExisting = true)
if (!file.exists()) {
avatarFound = false
Log.w("$TAG File [${file.absolutePath}] doesn't exist yet, trying to generate it")
val list = arrayListOf<String>()
for (participant in conferenceInfo.participantInfos) {
val friend = coreContext.contactsManager.findContactByAddress(
participant.address
)
if (friend != null) {
val picture = friend.photo
if (picture != null) {
list.add(picture)
}
}
}
if (list.isNotEmpty()) {
val bitmap = ImageUtils.generateBitmapFromList(list)
if (bitmap != null) {
val outputStream: OutputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
outputStream.close()
avatarFound = true
Log.i("$TAG Generated avatar and stored it in [${file.absolutePath}]")
} else {
Log.w("$TAG Can't generate avatar from that participants list")
}
} else {
Log.w(
"$TAG Can't generate avatar as no participant was found with an available picture"
)
}
}
if (avatarFound) {
fakeFriend.photo = FileUtils.getProperFilePath(file.absolutePath)
}
val avatarModel = ContactAvatarModel(fakeFriend)
avatarModel.defaultToConferenceIcon.postValue(true)
avatarModel.skipInitials.postValue(true)

View file

@ -22,7 +22,6 @@ package org.linphone.utils
import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.graphics.BitmapFactory
import android.os.Bundle
import androidx.annotation.WorkerThread
import androidx.collection.ArraySet
@ -141,18 +140,7 @@ class ShortcutUtils {
).buildIcon()
} else {
subject = chatRoom.subject.orEmpty()
val picture = ImageUtils.generateBitmapForChatRoom(chatRoom)
if (picture.isNotEmpty()) {
// BitmapFactory.decodeFile() doesn't handle file:/ URIs
val file = if (picture.startsWith("file:/")) {
picture.substring("file:/".length)
} else {
picture
}
IconCompat.createWithAdaptiveBitmap(BitmapFactory.decodeFile(file))
} else {
AvatarGenerator(context).setInitials(subject).buildIcon()
}
AvatarGenerator(context).setInitials(subject).buildIcon()
}
val persons = arrayOfNulls<Person>(personsList.size)

View file

@ -115,7 +115,7 @@
android:textSize="14sp"
android:drawableStart="@drawable/camera"
android:drawablePadding="3dp"
android:visibility="@{viewModel.accountModel.images.size() == 0 ? View.VISIBLE : View.GONE, default=gone}"
android:visibility="@{viewModel.accountModel.picturePath.length() == 0 ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toBottomOf="@id/avatar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
@ -131,7 +131,7 @@
android:textSize="14sp"
android:drawableStart="@drawable/pencil_simple"
android:drawablePadding="3dp"
android:visibility="@{viewModel.accountModel.images.size() == 0 ? View.GONE : View.VISIBLE}"
android:visibility="@{viewModel.accountModel.picturePath.length() == 0 ? View.GONE : View.VISIBLE}"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintTop_toBottomOf="@id/avatar"
app:layout_constraintStart_toStartOf="parent"
@ -149,7 +149,7 @@
android:textSize="14sp"
android:drawableStart="@drawable/trash_simple"
android:drawablePadding="3dp"
android:visibility="@{viewModel.accountModel.images.size() == 0 ? View.GONE : View.VISIBLE}"
android:visibility="@{viewModel.accountModel.picturePath.length() == 0 ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toBottomOf="@id/avatar"
app:layout_constraintStart_toEndOf="@id/edit_picture_label"
app:layout_constraintEnd_toEndOf="parent"/>