Updated adapters (no longer need viewlifecycleowner) & improved avatar loader

This commit is contained in:
Sylvain Berfini 2023-11-16 14:59:52 +01:00
parent 01dab1613d
commit 26b3fe67a3
18 changed files with 171 additions and 148 deletions

View file

@ -23,8 +23,8 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -33,7 +33,7 @@ import org.linphone.databinding.CallListCellBinding
import org.linphone.ui.call.model.CallModel
import org.linphone.utils.Event
class CallsListAdapter(private val viewLifecycleOwner: LifecycleOwner) :
class CallsListAdapter :
ListAdapter<CallModel, RecyclerView.ViewHolder>(CallDiffCallback()) {
var selectedAdapterPosition = -1
@ -54,7 +54,7 @@ class CallsListAdapter(private val viewLifecycleOwner: LifecycleOwner) :
)
val viewHolder = ViewHolder(binding)
binding.apply {
lifecycleOwner = viewLifecycleOwner
lifecycleOwner = parent.findViewTreeLifecycleOwner()
setOnClickListener {
callClickedEvent.value = Event(model!!)

View file

@ -23,6 +23,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -40,6 +41,7 @@ class ConferenceParticipantsListAdapter :
parent,
false
)
binding.lifecycleOwner = parent.findViewTreeLifecycleOwner()
return ViewHolder(binding)
}

View file

@ -42,6 +42,12 @@ class CallsListFragment : GenericCallFragment() {
private lateinit var adapter: CallsListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapter = CallsListAdapter()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -61,9 +67,7 @@ class CallsListFragment : GenericCallFragment() {
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
adapter = CallsListAdapter(viewLifecycleOwner)
binding.callsList.setHasFixedSize(true)
binding.callsList.adapter = adapter
binding.callsList.layoutManager = LinearLayoutManager(requireContext())
adapter.callLongClickedEvent.observe(viewLifecycleOwner) {
@ -89,6 +93,10 @@ class CallsListFragment : GenericCallFragment() {
viewModel.calls.observe(viewLifecycleOwner) {
Log.i("$TAG Calls list updated with [${it.size}] items")
adapter.submitList(it)
if (binding.callsList.adapter != adapter) {
binding.callsList.adapter = adapter
}
}
}
}

View file

@ -4,6 +4,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -22,6 +23,7 @@ class ChatMessageBottomSheetAdapter : ListAdapter<ChatMessageBottomSheetParticip
parent,
false
)
binding.lifecycleOwner = parent.findViewTreeLifecycleOwner()
return ViewHolder(binding)
}

View file

@ -22,8 +22,8 @@ package org.linphone.ui.main.chat.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -57,8 +57,6 @@ class ConversationEventAdapter : ListAdapter<EventLogModel, RecyclerView.ViewHol
MutableLiveData<Event<ChatMessageModel>>()
}
lateinit var viewLifecycleOwner: LifecycleOwner
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
INCOMING_CHAT_MESSAGE -> createIncomingChatBubble(parent)
@ -86,6 +84,8 @@ class ConversationEventAdapter : ListAdapter<EventLogModel, RecyclerView.ViewHol
)
val viewHolder = IncomingBubbleViewHolder(binding)
binding.apply {
lifecycleOwner = parent.findViewTreeLifecycleOwner()
setOnLongClickListener {
chatMessageLongPressEvent.value = Event(model!!)
true
@ -100,7 +100,6 @@ class ConversationEventAdapter : ListAdapter<EventLogModel, RecyclerView.ViewHol
setScrollToRepliedMessageClickListener {
scrollToRepliedMessageEvent.value = Event(model!!)
}
lifecycleOwner = viewLifecycleOwner
}
return viewHolder
}
@ -114,6 +113,8 @@ class ConversationEventAdapter : ListAdapter<EventLogModel, RecyclerView.ViewHol
)
val viewHolder = OutgoingBubbleViewHolder(binding)
binding.apply {
lifecycleOwner = parent.findViewTreeLifecycleOwner()
setOnLongClickListener {
chatMessageLongPressEvent.value = Event(model!!)
true
@ -128,7 +129,6 @@ class ConversationEventAdapter : ListAdapter<EventLogModel, RecyclerView.ViewHol
setScrollToRepliedMessageClickListener {
scrollToRepliedMessageEvent.value = Event(model!!)
}
lifecycleOwner = viewLifecycleOwner
}
return viewHolder
}
@ -140,7 +140,7 @@ class ConversationEventAdapter : ListAdapter<EventLogModel, RecyclerView.ViewHol
parent,
false
)
binding.lifecycleOwner = viewLifecycleOwner
binding.lifecycleOwner = parent.findViewTreeLifecycleOwner()
return EventViewHolder(binding)
}

View file

@ -4,6 +4,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -22,6 +23,7 @@ class ConversationParticipantsAdapter : ListAdapter<ParticipantModel, RecyclerVi
parent,
false
)
binding.lifecycleOwner = parent.findViewTreeLifecycleOwner()
return ViewHolder(binding)
}

View file

@ -4,8 +4,8 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -14,9 +14,9 @@ import org.linphone.databinding.ChatListCellBinding
import org.linphone.ui.main.chat.model.ConversationModel
import org.linphone.utils.Event
class ConversationsListAdapter(
private val viewLifecycleOwner: LifecycleOwner
) : ListAdapter<ConversationModel, RecyclerView.ViewHolder>(ChatRoomDiffCallback()) {
class ConversationsListAdapter : ListAdapter<ConversationModel, RecyclerView.ViewHolder>(
ChatRoomDiffCallback()
) {
var selectedAdapterPosition = -1
val conversationClickedEvent: MutableLiveData<Event<ConversationModel>> by lazy {
@ -36,7 +36,7 @@ class ConversationsListAdapter(
)
val viewHolder = ViewHolder(binding)
binding.apply {
lifecycleOwner = viewLifecycleOwner
lifecycleOwner = parent.findViewTreeLifecycleOwner()
setOnClickListener {
conversationClickedEvent.value = Event(model!!)

View file

@ -203,7 +203,6 @@ class ConversationFragment : GenericFragment() {
viewModel.showBackButton.value = slideable
}
adapter.viewLifecycleOwner = viewLifecycleOwner
binding.eventsList.setHasFixedSize(true)
binding.eventsList.layoutManager = LinearLayoutManager(requireContext())

View file

@ -59,6 +59,12 @@ class ConversationsListFragment : AbstractTopBarFragment() {
listViewModel.applyFilter()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapter = ConversationsListAdapter()
}
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
if (
findNavController().currentDestination?.id == R.id.startConversationFragment ||
@ -88,9 +94,7 @@ class ConversationsListFragment : AbstractTopBarFragment() {
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = listViewModel
adapter = ConversationsListAdapter(viewLifecycleOwner)
binding.conversationsList.setHasFixedSize(true)
binding.conversationsList.adapter = adapter
binding.conversationsList.layoutManager = LinearLayoutManager(requireContext())
adapter.conversationLongClickedEvent.observe(viewLifecycleOwner) {
@ -151,6 +155,10 @@ class ConversationsListFragment : AbstractTopBarFragment() {
adapter.submitList(it)
Log.i("$TAG Conversations list ready with [${it.size}] items")
if (binding.conversationsList.adapter != adapter) {
binding.conversationsList.adapter = adapter
}
if (currentCount < it.size) {
binding.conversationsList.scrollToPosition(0)
}

View file

@ -4,8 +4,8 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -17,7 +17,6 @@ import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.utils.Event
class ContactsListAdapter(
private val viewLifecycleOwner: LifecycleOwner,
private val favourites: Boolean = false,
private val disableLongClick: Boolean = false
) : ListAdapter<ContactAvatarModel, RecyclerView.ViewHolder>(ContactDiffCallback()) {
@ -41,7 +40,7 @@ class ContactsListAdapter(
)
val viewHolder = FavouriteViewHolder(binding)
binding.apply {
lifecycleOwner = viewLifecycleOwner
lifecycleOwner = parent.findViewTreeLifecycleOwner()
setOnClickListener {
contactClickedEvent.value = Event(model!!)
@ -64,7 +63,7 @@ class ContactsListAdapter(
)
val viewHolder = ViewHolder(binding)
binding.apply {
lifecycleOwner = viewLifecycleOwner
lifecycleOwner = parent.findViewTreeLifecycleOwner()
setOnClickListener {
contactClickedEvent.value = Event(model!!)

View file

@ -67,6 +67,13 @@ class ContactsListFragment : AbstractTopBarFragment() {
listViewModel.applyCurrentDefaultAccountFilter()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapter = ContactsListAdapter()
favouritesAdapter = ContactsListAdapter(favourites = true)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -85,21 +92,17 @@ class ContactsListFragment : AbstractTopBarFragment() {
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = listViewModel
adapter = ContactsListAdapter(viewLifecycleOwner)
binding.contactsList.setHasFixedSize(true)
binding.contactsList.adapter = adapter
configureAdapter(adapter)
binding.contactsList.layoutManager = LinearLayoutManager(requireContext())
favouritesAdapter = ContactsListAdapter(viewLifecycleOwner, favourites = true)
binding.favouritesContactsList.setHasFixedSize(true)
binding.favouritesContactsList.adapter = favouritesAdapter
configureAdapter(favouritesAdapter)
val favouritesLayoutManager = LinearLayoutManager(requireContext())
favouritesLayoutManager.orientation = LinearLayoutManager.HORIZONTAL
binding.favouritesContactsList.layoutManager = favouritesLayoutManager
configureAdapter(adapter)
configureAdapter(favouritesAdapter)
listViewModel.contactsList.observe(
viewLifecycleOwner
) {
@ -107,6 +110,10 @@ class ContactsListFragment : AbstractTopBarFragment() {
adapter.submitList(it)
Log.i("$TAG Contacts list updated with [${it.size}] items")
if (binding.contactsList.adapter != adapter) {
binding.contactsList.adapter = adapter
}
if (currentCount < it.size) {
Log.i("$TAG Contacts list updated with new items, scrolling to top")
binding.contactsList.smoothScrollToPosition(0)
@ -122,6 +129,10 @@ class ContactsListFragment : AbstractTopBarFragment() {
viewLifecycleOwner
) {
favouritesAdapter.submitList(it)
if (binding.favouritesContactsList.adapter != favouritesAdapter) {
binding.favouritesContactsList.adapter = favouritesAdapter
}
Log.i("$TAG Favourites contacts list updated with [${it.size}] items")
}

View file

@ -23,6 +23,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -40,7 +41,7 @@ class ContactHistoryListAdapter : ListAdapter<CallLogHistoryModel, RecyclerView.
parent,
false
)
// binding.lifecycleOwner = viewLifecycleOwner
binding.lifecycleOwner = parent.findViewTreeLifecycleOwner()
return ViewHolder(binding)
}

View file

@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -73,6 +74,7 @@ class ContactsAndSuggestionsListAdapter :
parent,
false
)
binding.lifecycleOwner = parent.findViewTreeLifecycleOwner()
ContactViewHolder(binding)
}
else -> {
@ -83,6 +85,8 @@ class ContactsAndSuggestionsListAdapter :
false
)
binding.apply {
lifecycleOwner = parent.findViewTreeLifecycleOwner()
setOnClickListener {
contactClickedEvent.value = Event(model!!)
}

View file

@ -5,6 +5,7 @@ import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -37,6 +38,8 @@ class HistoryListAdapter : ListAdapter<CallLogModel, RecyclerView.ViewHolder>(Ca
)
val viewHolder = ViewHolder(binding)
binding.apply {
lifecycleOwner = parent.findViewTreeLifecycleOwner()
setOnClickListener {
callLogClickedEvent.value = Event(model!!)
}

View file

@ -52,7 +52,6 @@ class CallLogModel @WorkerThread constructor(private val callLog: CallLog) {
dateTime.postValue("$date | $time")
if (callLog.wasConference()) {
val conferenceInfo = coreContext.core.findConferenceInformationFromUri(address)
if (conferenceInfo != null) {
avatarModel = coreContext.contactsManager.getContactAvatarModelForConferenceInfo(

View file

@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
@ -53,6 +54,8 @@ class MeetingsListAdapter :
false
)
binding.apply {
lifecycleOwner = parent.findViewTreeLifecycleOwner()
setOnClickListener {
meetingClickedEvent.value = Event(model!!)
}

View file

@ -38,7 +38,6 @@ import androidx.annotation.UiThread
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatEditText
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.doOnLayout
@ -53,10 +52,8 @@ import coil.dispose
import coil.load
import coil.request.videoFrameMillis
import coil.size.Dimension
import coil.transform.RoundedCornersTransformation
import com.google.android.material.imageview.ShapeableImageView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.BR
@ -246,13 +243,13 @@ private fun loadImageForChatBubble(imageView: ImageView, file: String?, grid: Bo
}
val width = if (grid) Dimension(dimen) else Dimension.Undefined
val height = Dimension(dimen)
val radius = imageView.resources.getDimension(
/* val radius = imageView.resources.getDimension(
R.dimen.chat_bubble_images_rounded_corner_radius
)
) */
if (FileUtils.isExtensionVideo(file)) {
imageView.load(file) {
videoFrameMillis(0)
transformations(RoundedCornersTransformation(radius))
// transformations(RoundedCornersTransformation(radius))
size(width, height)
listener(
onError = { _, result ->
@ -295,52 +292,37 @@ fun ShapeableImageView.loadCircleFileWithCoil(file: String?) {
@UiThread
@BindingAdapter("coilAvatar")
fun ShapeableImageView.loadAvatarWithCoil(model: AbstractAvatarModel?) {
val imageView = this
(context as AppCompatActivity).lifecycleScope.launch {
loadContactPictureWithCoil(imageView, model)
}
loadContactPictureWithCoil(this, model)
}
@UiThread
@BindingAdapter("coilAvatarNoTrust")
fun ShapeableImageView.loadAvatarWithCoilWithoutTrust(model: AbstractAvatarModel?) {
val imageView = this
(context as AppCompatActivity).lifecycleScope.launch {
loadContactPictureWithCoil(imageView, model, skipTrust = true)
}
loadContactPictureWithCoil(this, model, skipTrust = true)
}
@UiThread
@BindingAdapter("coilBubbleAvatar")
fun ShapeableImageView.loadBubbleAvatarWithCoil(model: AbstractAvatarModel?) {
val imageView = this
(context as AppCompatActivity).lifecycleScope.launch {
val size = R.dimen.avatar_bubble_size
val initialsSize = R.dimen.avatar_initials_bubble_text_size
loadContactPictureWithCoil(imageView, model, size = size, textSize = initialsSize)
}
val size = R.dimen.avatar_bubble_size
val initialsSize = R.dimen.avatar_initials_bubble_text_size
loadContactPictureWithCoil(this, model, size = size, textSize = initialsSize)
}
@UiThread
@BindingAdapter("coilBigAvatar")
fun ShapeableImageView.loadBigAvatarWithCoil(model: AbstractAvatarModel?) {
val imageView = this
(context as AppCompatActivity).lifecycleScope.launch {
val size = R.dimen.avatar_big_size
val initialsSize = R.dimen.avatar_initials_big_text_size
loadContactPictureWithCoil(imageView, model, size = size, textSize = initialsSize)
}
val size = R.dimen.avatar_big_size
val initialsSize = R.dimen.avatar_initials_big_text_size
loadContactPictureWithCoil(this, model, size = size, textSize = initialsSize)
}
@UiThread
@BindingAdapter("coilCallAvatar")
fun ShapeableImageView.loadCallAvatarWithCoil(model: AbstractAvatarModel?) {
val imageView = this
(context as AppCompatActivity).lifecycleScope.launch {
val size = R.dimen.avatar_in_call_size
val initialsSize = R.dimen.avatar_initials_call_text_size
loadContactPictureWithCoil(imageView, model, size = size, textSize = initialsSize)
}
val size = R.dimen.avatar_in_call_size
val initialsSize = R.dimen.avatar_initials_call_text_size
loadContactPictureWithCoil(this, model, size = size, textSize = initialsSize)
}
@UiThread
@ -356,109 +338,92 @@ fun ShapeableImageView.loadInitialsAvatarWithCoil(initials: String?) {
}
@SuppressLint("ResourceType")
private suspend fun loadContactPictureWithCoil(
private fun loadContactPictureWithCoil(
imageView: ShapeableImageView,
model: AbstractAvatarModel?,
@DimenRes size: Int = 0,
@DimenRes textSize: Int = 0,
skipTrust: Boolean = false
) {
withContext(Dispatchers.IO) {
imageView.dispose()
imageView.dispose()
val context = imageView.context
if (model != null) {
if (model.forceConferenceIcon.value == true) {
imageView.load(
ResourcesCompat.getDrawable(
context.resources,
R.drawable.inset_users_three,
context.theme
)
)
return@withContext
}
val context = imageView.context
if (model != null) {
if (model.forceConferenceIcon.value == true) {
imageView.load(R.drawable.inset_users_three)
return
}
if (!skipTrust) {
if (model.showTrust.value == true) {
when (model.trust.value) {
ChatRoom.SecurityLevel.Safe -> {
imageView.setStrokeColorResource(R.color.blue_info_500)
imageView.setStrokeWidthResource(R.dimen.avatar_trust_border_width)
}
ChatRoom.SecurityLevel.Unsafe -> {
imageView.setStrokeColorResource(R.color.red_danger_500)
imageView.setStrokeWidthResource(R.dimen.avatar_trust_border_width)
}
else -> {
imageView.setStrokeColorResource(R.color.transparent_color)
imageView.setStrokeWidthResource(R.dimen.zero)
}
if (!skipTrust) {
if (model.showTrust.value == true) {
when (model.trust.value) {
ChatRoom.SecurityLevel.Safe -> {
imageView.setStrokeColorResource(R.color.blue_info_500)
imageView.setStrokeWidthResource(R.dimen.avatar_trust_border_width)
}
} else {
imageView.setStrokeColorResource(R.color.transparent_color)
imageView.setStrokeWidthResource(R.dimen.zero)
}
}
val images = model.images.value.orEmpty()
val count = images.size
if (count == 1) {
val image = images.firstOrNull()
ChatRoom.SecurityLevel.Unsafe -> {
imageView.setStrokeColorResource(R.color.red_danger_500)
imageView.setStrokeWidthResource(R.dimen.avatar_trust_border_width)
}
else -> {
imageView.setStrokeColorResource(R.color.transparent_color)
imageView.setStrokeWidthResource(R.dimen.zero)
}
}
} else {
imageView.setStrokeColorResource(R.color.transparent_color)
imageView.setStrokeWidthResource(R.dimen.zero)
}
}
val images = model.images.value.orEmpty()
val count = images.size
if (count == 1) {
val image = images.firstOrNull()
if (image != null) {
imageView.load(image) {
error(
coroutineScope {
withContext(Dispatchers.IO) {
val initials = model.initials.value.orEmpty()
if (initials.isEmpty() || initials == "+" || model.skipInitials.value == true) {
if (model.defaultToConferenceIcon.value == true) {
ResourcesCompat.getDrawable(
context.resources,
R.drawable.inset_users_three,
context.theme
)
} else {
ResourcesCompat.getDrawable(
context.resources,
R.drawable.inset_user_circle,
context.theme
)
}
} else {
val builder = AvatarGenerator(context)
builder.setInitials(model.initials.value.orEmpty())
if (size > 0) {
builder.setAvatarSize(AppUtils.getDimension(size).toInt())
}
if (textSize > 0) {
builder.setTextSize(AppUtils.getDimension(textSize))
}
builder.build()
}
}
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()
}
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()
}
(context as AppCompatActivity).lifecycleScope.launch {
val bitmap = ImageUtils.getBitmapFromMultipleAvatars(imageView.context, w, images)
imageView.load(bitmap)
}
} else {
imageView.load(
ResourcesCompat.getDrawable(
context.resources,
R.drawable.inset_user_circle,
context.theme
)
)
}
} else {
imageView.load(R.drawable.smiley)
}
}
private fun getErrorImageLoader(
context: Context,
model: AbstractAvatarModel,
size: Int,
textSize: Int
): Any {
val initials = model.initials.value.orEmpty()
return if (initials.isEmpty() || initials == "+" || model.skipInitials.value == true) {
if (model.defaultToConferenceIcon.value == true) {
R.drawable.inset_users_three
} else {
R.drawable.inset_user_circle
}
} else {
ImageUtils.getGeneratedAvatar(context, size, textSize, initials)
}
}

View file

@ -24,6 +24,7 @@ import android.graphics.Bitmap
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
@ -31,12 +32,28 @@ import androidx.core.graphics.drawable.toBitmap
import coil.imageLoader
import coil.request.ImageRequest
import java.io.FileNotFoundException
import org.linphone.contacts.AvatarGenerator
import org.linphone.core.tools.Log
class ImageUtils {
companion object {
private const val TAG = "[Image Utils]"
@WorkerThread
fun getGeneratedAvatar(context: Context, size: Int = 0, textSize: Int = 0, initials: String): BitmapDrawable {
val builder = AvatarGenerator(context)
builder.setInitials(initials)
if (size > 0) {
builder.setAvatarSize(
AppUtils.getDimension(size).toInt()
)
}
if (textSize > 0) {
builder.setTextSize(AppUtils.getDimension(textSize))
}
return builder.build()
}
@WorkerThread
fun getBitmap(
context: Context,