srctree

Roee Hershberg parent 34114699 3073f386
Refactor AvatarFactory to the purpose of only creating avatars

inlinesplit
atox/src/androidTest/kotlin/ui/AvatarFactoryTest.kt added: 85, removed: 60, total 25
@@ -4,10 +4,8 @@
 
package ltd.evilcorp.atox.ui
 
import android.widget.ImageView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import ltd.evilcorp.core.vo.Contact
import org.junit.Test
import org.junit.runner.RunWith
 
@@ -15,13 +13,19 @@ import org.junit.runner.RunWith
class AvatarFactoryTest {
@Test
fun emptyName() {
val imageView = ImageView(InstrumentationRegistry.getInstrumentation().targetContext)
AvatarFactory(Contact(publicKey = "123")).assignInto(imageView)
AvatarFactory.create(
InstrumentationRegistry.getInstrumentation().targetContext.resources,
name = "",
publicKey = "123",
)
}
 
@Test
fun nameEndingInSpace() {
val imageView = ImageView(InstrumentationRegistry.getInstrumentation().targetContext)
AvatarFactory(Contact(publicKey = "123", name = "a ")).assignInto(imageView)
AvatarFactory.create(
InstrumentationRegistry.getInstrumentation().targetContext.resources,
name = "a ",
publicKey = "123",
)
}
}
 
atox/src/main/kotlin/ui/AvatarFactory.kt added: 85, removed: 60, total 25
@@ -4,6 +4,7 @@
 
package ltd.evilcorp.atox.ui
 
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
@@ -11,60 +12,48 @@ import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.Typeface
import android.net.Uri
import android.widget.ImageView
import kotlin.math.abs
import ltd.evilcorp.atox.R
import ltd.evilcorp.core.vo.Contact
 
// Class for creating an avatar for contact and assigning it into an ImageView
internal class AvatarFactory(contact: Contact) {
internal object AvatarFactory {
 
private val name: String = contact.name
private val publicKey: String = contact.publicKey
private val avatarUri: String = contact.avatarUri
 
private fun getInitials(): String {
private fun getInitials(name: String): String {
val segments = name.split(" ")
if (segments.size == 1) return segments.first().take(1)
return segments.first().take(1) + segments[1].take(1)
}
 
/*
* Method will assign an avatar to an image view. If avatar image has been set to the contact
* then it will be set to the image view, otherwise a new avatar image will be created based
* on the initials of the name and the public key for the background color.
*/
fun assignInto(
imageView: ImageView,
size: Size = Px(imageView.resources.getDimension(R.dimen.default_avatar_size).toInt())
) =
if (avatarUri.isNotEmpty()) {
imageView.setImageURI(Uri.parse(avatarUri))
} else {
val defaultAvatarSize = imageView.resources.getDimension(R.dimen.default_avatar_size).toInt()
val side = size.asPx(imageView.resources).px
val textScale = side / defaultAvatarSize
// Method will create an avatar based on the initials of a name and a public key for the background color.
fun create(
resources: Resources,
name: String,
publicKey: String,
size: Size = Px(resources.getDimension(R.dimen.default_avatar_size).toInt())
): Bitmap {
val defaultAvatarSize = resources.getDimension(R.dimen.default_avatar_size)
val sizePx = size.asPx(resources).px
val textScale = sizePx / defaultAvatarSize
 
val bitmap = Bitmap.createBitmap(side, side, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat())
val colors = imageView.resources.getIntArray(R.array.contactBackgrounds)
val backgroundPaint = Paint().apply { color = colors[abs(publicKey.hashCode()).rem(colors.size)] }
val bitmap = Bitmap.createBitmap(sizePx, sizePx, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
val rect = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat())
val colors = resources.getIntArray(R.array.contactBackgrounds)
val backgroundPaint = Paint().apply { color = colors[abs(publicKey.hashCode()).rem(colors.size)] }
 
val textPaint = Paint().apply {
color = Color.WHITE
textSize = imageView.resources.getDimension(R.dimen.contact_avatar_placeholder_text) * textScale
textAlign = Paint.Align.CENTER
isAntiAlias = true
typeface = Typeface.create("sans-serif-light", Typeface.NORMAL)
}
 
val textBounds = Rect()
val initials = getInitials()
textPaint.getTextBounds(initials, 0, initials.length, textBounds)
canvas.drawRoundRect(rect, rect.bottom, rect.right, backgroundPaint)
canvas.drawText(initials, rect.centerX(), rect.centerY() - textBounds.exactCenterY(), textPaint)
imageView.setImageBitmap(bitmap)
val textPaint = Paint().apply {
color = Color.WHITE
textSize = resources.getDimension(R.dimen.contact_avatar_placeholder_text) * textScale
textAlign = Paint.Align.CENTER
isAntiAlias = true
typeface = Typeface.create("sans-serif-light", Typeface.NORMAL)
}
 
val textBounds = Rect()
val initials = getInitials(name)
textPaint.getTextBounds(initials, 0, initials.length, textBounds)
canvas.drawRoundRect(rect, rect.bottom, rect.right, backgroundPaint)
canvas.drawText(initials, rect.centerX(), rect.centerY() - textBounds.exactCenterY(), textPaint)
 
return bitmap
}
}
 
atox/src/main/kotlin/ui/call/CallFragment.kt added: 85, removed: 60, total 25
@@ -5,6 +5,7 @@
package ltd.evilcorp.atox.ui.call
 
import android.Manifest
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.Toast
@@ -53,7 +54,18 @@ class CallFragment : BaseFragment<FragmentCallBinding>(FragmentCallBinding::infl
 
vm.setActiveContact(PublicKey(requireStringArg(CONTACT_PUBLIC_KEY)))
vm.contact.observe(viewLifecycleOwner) {
AvatarFactory(it).assignInto(callBackground, Dp(CALL_BACKGROUND_SIZE_DP))
if (it.avatarUri.isNotEmpty()) {
callBackground.setImageURI(Uri.parse(it.avatarUri))
} else {
callBackground.setImageBitmap(
AvatarFactory.create(
resources,
it.name,
it.publicKey,
Dp(CALL_BACKGROUND_SIZE_DP)
)
)
}
}
 
endCall.setOnClickListener {
 
atox/src/main/kotlin/ui/chat/ChatFragment.kt added: 85, removed: 60, total 25
@@ -190,7 +190,11 @@ class ChatFragment : BaseFragment<FragmentChatBinding>(FragmentChatBinding::infl
}.lowercase(Locale.getDefault())
 
profileLayout.statusIndicator.setColorFilter(colorByContactStatus(requireContext(), it))
AvatarFactory(it).assignInto(profileLayout.profileImage)
if (it.avatarUri.isNotEmpty()) {
profileLayout.profileImage.setImageURI(Uri.parse(it.avatarUri))
} else {
profileLayout.profileImage.setImageBitmap(AvatarFactory.create(resources, it.name, it.publicKey))
}
 
if (it.draftMessage.isNotEmpty() && outgoingMessage.text.isEmpty()) {
outgoingMessage.setText(it.draftMessage)
 
atox/src/main/kotlin/ui/contact_profile/ContactProfileFragment.kt added: 85, removed: 60, total 25
@@ -4,6 +4,7 @@
 
package ltd.evilcorp.atox.ui.contact_profile
 
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.core.view.ViewCompat
@@ -42,7 +43,17 @@ class ContactProfileFragment : BaseFragment<FragmentContactProfileBinding>(Fragm
contact.name = contact.name.ifEmpty { getString(R.string.contact_default_name) }
 
headerMainText.text = contact.name
AvatarFactory(contact).assignInto(profileLayout.profileImage)
if (contact.avatarUri.isNotEmpty()) {
profileLayout.profileImage.setImageURI(Uri.parse(contact.avatarUri))
} else {
profileLayout.profileImage.setImageBitmap(
AvatarFactory.create(
resources,
contact.name,
contact.publicKey
)
)
}
profileLayout.statusIndicator.setColorFilter(colorByContactStatus(requireContext(), contact))
 
contactPublicKey.text = contact.publicKey
 
atox/src/main/kotlin/ui/contactlist/ContactAdapter.kt added: 85, removed: 60, total 25
@@ -5,6 +5,7 @@
package ltd.evilcorp.atox.ui.contactlist
 
import android.content.Context
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -112,7 +113,11 @@ class ContactAdapter(
}
}
vh.status.setColorFilter(colorByContactStatus(context, this))
AvatarFactory(this).assignInto(vh.image)
if (avatarUri.isNotEmpty()) {
vh.image.setImageURI(Uri.parse(avatarUri))
} else {
vh.image.setImageBitmap(AvatarFactory.create(vh.image.resources, name, publicKey))
}
vh.unreadIndicator.visibility = if (hasUnreadMessages) {
View.VISIBLE
} else {