session-android/app/src/main/java/org/thoughtcrime/securesms/util/AvatarPlaceholderGenerator.kt

101 lines
3.8 KiB
Kotlin

package org.thoughtcrime.securesms.util
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.Typeface
import android.graphics.drawable.BitmapDrawable
import android.text.TextPaint
import android.text.TextUtils
import network.loki.messenger.R
import java.math.BigInteger
import java.security.MessageDigest
import java.util.Locale
object AvatarPlaceholderGenerator {
private const val EMPTY_LABEL = "0"
@JvmStatic
fun generate(context: Context, pixelSize: Int, hashString: String, displayName: String?): BitmapDrawable {
val hash: Long
if (hashString.length >= 12 && hashString.matches(Regex("^[0-9A-Fa-f]+\$"))) {
hash = getSha512(hashString).substring(0 until 12).toLong(16)
} else {
hash = 0
}
// Do not cache color array, it may be different depends on the current theme.
val colorArray = context.resources.getIntArray(R.array.profile_picture_placeholder_colors)
val colorPrimary = colorArray[(hash % colorArray.size).toInt()]
val labelText = when {
!TextUtils.isEmpty(displayName) -> extractLabel(displayName!!.capitalize(Locale.ROOT))
!TextUtils.isEmpty(hashString) -> extractLabel(hashString)
else -> EMPTY_LABEL
}
val bitmap = Bitmap.createBitmap(pixelSize, pixelSize, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
// Draw background/frame
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.color = colorPrimary
canvas.drawCircle(pixelSize.toFloat() / 2, pixelSize.toFloat() / 2, pixelSize.toFloat() / 2, paint)
// Draw text
val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
textPaint.typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
textPaint.textSize = pixelSize * 0.5f
textPaint.color = Color.WHITE
val areaRect = Rect(0, 0, pixelSize, pixelSize)
val textBounds = RectF(areaRect)
textBounds.right = textPaint.measureText(labelText)
textBounds.bottom = textPaint.descent() - textPaint.ascent()
textBounds.left += (areaRect.width() - textBounds.right) * 0.5f
textBounds.top += (areaRect.height() - textBounds.bottom) * 0.5f
canvas.drawText(labelText, textBounds.left, textBounds.top - textPaint.ascent(), textPaint)
return BitmapDrawable(context.resources, bitmap)
}
fun extractLabel(content: String): String {
val trimmedContent = content.trim()
if (trimmedContent.isEmpty()) return EMPTY_LABEL
return if (trimmedContent.length > 2 && trimmedContent.startsWith("05")) {
trimmedContent[2].toString()
} else {
val splitWords = trimmedContent.split(Regex("\\W"))
if (splitWords.size < 2) {
trimmedContent.take(2)
} else {
splitWords.filter { word -> word.isNotEmpty() }.take(2).map { it.first() }.joinToString("")
}
}.uppercase()
}
private fun getSha512(input: String): String {
val messageDigest = MessageDigest.getInstance("SHA-512").digest(input.toByteArray())
// Convert byte array into signum representation
val no = BigInteger(1, messageDigest)
// Convert message digest into hex value
var hashText: String = no.toString(16)
// Add preceding 0s to make it 32 bytes
if (hashText.length < 128) {
val sb = StringBuilder()
for (i in 0 until 128 - hashText.length) {
sb.append('0')
}
hashText = sb.append(hashText).toString()
}
return hashText
}
}