From b58a23b60d17617dc9b28e19da7dad367ee21429 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 12 Aug 2024 14:36:41 +0200 Subject: [PATCH] Fixed Android Auto favorites no having generated avatar if no picture available + make them round --- .../org/linphone/contacts/AvatarGenerator.kt | 8 ++-- .../org/linphone/contacts/ContactsManager.kt | 5 ++- .../org/linphone/telecom/auto/AAScreen.kt | 38 ++++++++++-------- .../org/linphone/utils/DataBindingUtils.kt | 2 +- .../java/org/linphone/utils/ImageUtils.kt | 40 +++++++++++++++++-- 5 files changed, 67 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/linphone/contacts/AvatarGenerator.kt b/app/src/main/java/org/linphone/contacts/AvatarGenerator.kt index c67069226..ab787addb 100644 --- a/app/src/main/java/org/linphone/contacts/AvatarGenerator.kt +++ b/app/src/main/java/org/linphone/contacts/AvatarGenerator.kt @@ -48,7 +48,7 @@ class AvatarGenerator(private val context: Context) { initials = label } - private fun preBuild(): Bitmap { + fun buildBitmap(): Bitmap { val textPainter = getTextPainter() val painter = getPainter() @@ -68,12 +68,12 @@ class AvatarGenerator(private val context: Context) { return bitmap } - fun build(): BitmapDrawable { - return BitmapDrawable(context.resources, preBuild()) + fun buildDrawable(): BitmapDrawable { + return BitmapDrawable(context.resources, buildBitmap()) } fun buildIcon(): IconCompat { - return IconCompat.createWithAdaptiveBitmap(preBuild()) + return IconCompat.createWithAdaptiveBitmap(buildBitmap()) } private fun getTextPainter(): TextPaint { diff --git a/app/src/main/java/org/linphone/contacts/ContactsManager.kt b/app/src/main/java/org/linphone/contacts/ContactsManager.kt index 9b4797ad3..61c6bfda6 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsManager.kt +++ b/app/src/main/java/org/linphone/contacts/ContactsManager.kt @@ -647,11 +647,12 @@ class ContactsManager @UiThread constructor() { } @WorkerThread -fun Friend.getAvatarBitmap(): Bitmap? { +fun Friend.getAvatarBitmap(round: Boolean = false): Bitmap? { try { return ImageUtils.getBitmap( coreContext.context, - photo ?: getNativeContactPictureUri()?.toString() + photo ?: getNativeContactPictureUri()?.toString(), + round ) } catch (numberFormatException: NumberFormatException) { // Expected for contacts created by Linphone diff --git a/app/src/main/java/org/linphone/telecom/auto/AAScreen.kt b/app/src/main/java/org/linphone/telecom/auto/AAScreen.kt index 20340f6c3..011e1e8ab 100644 --- a/app/src/main/java/org/linphone/telecom/auto/AAScreen.kt +++ b/app/src/main/java/org/linphone/telecom/auto/AAScreen.kt @@ -31,9 +31,11 @@ import androidx.car.app.model.Template import androidx.core.graphics.drawable.IconCompat import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.R -import org.linphone.contacts.getNativeContactPictureUri +import org.linphone.contacts.AvatarGenerator +import org.linphone.contacts.getAvatarBitmap import org.linphone.core.MagicSearch import org.linphone.core.tools.Log +import org.linphone.utils.AppUtils import org.linphone.utils.LinphoneUtils class AAScreen(context: CarContext) : Screen(context) { @@ -63,20 +65,20 @@ class AAScreen(context: CarContext) : Screen(context) { val friend = result.friend ?: continue builder.setTitle(friend.name) - val pictureUri = friend.getNativeContactPictureUri() - if (pictureUri != null) { - Log.i( - "$TAG Creating car icon for friend [${friend.name}] with URI [$pictureUri]" + Log.i("$TAG Creating car icon for friend [${friend.name}]") + try { + val bitmap = friend.getAvatarBitmap(true) ?: AvatarGenerator( + coreContext.context + ).setInitials( + AppUtils.getInitials(friend.name.orEmpty()) + ).buildBitmap() + builder.setImage( + CarIcon.Builder(IconCompat.createWithBitmap(bitmap)) + .build(), + GridItem.IMAGE_TYPE_LARGE ) - try { - builder.setImage( - CarIcon.Builder(IconCompat.createWithContentUri(pictureUri)) - .build(), - GridItem.IMAGE_TYPE_LARGE - ) - } catch (e: Exception) { - Log.e("$TAG Exception trying to create CarIcon: $e") - } + } catch (e: Exception) { + Log.e("$TAG Exception trying to create CarIcon: $e") } builder.setOnClickListener { @@ -86,8 +88,12 @@ class AAScreen(context: CarContext) : Screen(context) { coreContext.startAudioCall(address) } } - val item = builder.build() - favorites.add(item) + try { + val item = builder.build() + favorites.add(item) + } catch (e: Exception) { + Log.e("$TAG Failed to build grid item: $e") + } } loading = false Log.i("$TAG Processed [${favorites.size}] favorites") diff --git a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt index cbd803557..0a49f185c 100644 --- a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt +++ b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt @@ -414,7 +414,7 @@ fun ImageView.loadCallAvatarWithCoil(model: AbstractAvatarModel?) { fun ImageView.loadInitialsAvatarWithCoil(initials: String?) { val builder = AvatarGenerator(context) builder.setInitials(initials.orEmpty()) - load(builder.build()) + load(builder.buildDrawable()) } @SuppressLint("ResourceType") diff --git a/app/src/main/java/org/linphone/utils/ImageUtils.kt b/app/src/main/java/org/linphone/utils/ImageUtils.kt index 1acfe0367..ad89f3b4f 100644 --- a/app/src/main/java/org/linphone/utils/ImageUtils.kt +++ b/app/src/main/java/org/linphone/utils/ImageUtils.kt @@ -21,7 +21,12 @@ 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.graphics.drawable.BitmapDrawable import android.net.Uri import androidx.annotation.AnyThread @@ -46,13 +51,14 @@ class ImageUtils { if (textSize > 0) { builder.setTextSize(AppUtils.getDimension(textSize)) } - return builder.build() + return builder.buildDrawable() } @WorkerThread fun getBitmap( context: Context, - path: String? + path: String?, + round: Boolean = false ): Bitmap? { Log.d("$TAG Trying to create Bitmap from path [$path]") if (path != null) { @@ -64,12 +70,17 @@ class ImageUtils { } // We make a copy to ensure Bitmap will be Software and not Hardware, required for shortcuts - return ImageDecoder.decodeBitmap( + val bitmap = ImageDecoder.decodeBitmap( ImageDecoder.createSource(context.contentResolver, fromPictureUri) ).copy( Bitmap.Config.ARGB_8888, true ) + return if (round) { + getRoundBitmap(bitmap) + } else { + bitmap + } } catch (fnfe: FileNotFoundException) { Log.e("$TAG File [$path] not found: $fnfe") return null @@ -82,5 +93,28 @@ class ImageUtils { Log.e("$TAG Can't get bitmap from null URI") return null } + + @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 + } } }