mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Removed avatar generation for conversations/meetings depending on members avatars
This commit is contained in:
parent
81af7a8bc0
commit
d848622ace
12 changed files with 19 additions and 231 deletions
|
|
@ -30,7 +30,7 @@ abstract class AbstractAvatarModel {
|
|||
|
||||
val initials = MutableLiveData<String>()
|
||||
|
||||
val images = MutableLiveData<ArrayList<String>>()
|
||||
val picturePath = MutableLiveData<String>()
|
||||
|
||||
val forceConversationIcon = MutableLiveData<Boolean>()
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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]")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue