Optimized decoding of bitmaps
This commit is contained in:
@ -3,7 +3,6 @@ package com.tutpro.baresip
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.ContactsContract
|
import android.provider.ContactsContract
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
@ -302,7 +301,7 @@ sealed class Contact {
|
|||||||
val avatarFilePath = BaresipService.filesPath + "/$id.png"
|
val avatarFilePath = BaresipService.filesPath + "/$id.png"
|
||||||
if (File(avatarFilePath).exists()) {
|
if (File(avatarFilePath).exists()) {
|
||||||
try {
|
try {
|
||||||
contact.avatarImage = BitmapFactory.decodeFile(avatarFilePath)
|
contact.avatarImage = Utils.decodeSampledBitmapFromFile(avatarFilePath, 192, 192)
|
||||||
Log.d(TAG, "Set avatarImage")
|
Log.d(TAG, "Set avatarImage")
|
||||||
if (contact.avatarImage == null)
|
if (contact.avatarImage == null)
|
||||||
Log.d(TAG, "Contact $id avatarImage is null")
|
Log.d(TAG, "Contact $id avatarImage is null")
|
||||||
|
|||||||
@ -468,9 +468,7 @@ private fun AvatarSection(
|
|||||||
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
try {
|
try {
|
||||||
val inputStream = ctx.contentResolver.openInputStream(uri)
|
val avatarBitmap = Utils.decodeSampledBitmapFromUri(ctx, uri, 192, 192)
|
||||||
val avatarBitmap = BitmapFactory.decodeStream(inputStream)
|
|
||||||
inputStream?.close()
|
|
||||||
|
|
||||||
if (avatarBitmap == null) {
|
if (avatarBitmap == null) {
|
||||||
Log.e(TAG, "Failed to decode bitmap from URI: $uri")
|
Log.e(TAG, "Failed to decode bitmap from URI: $uri")
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.BitmapFactory
|
import androidx.core.graphics.scale
|
||||||
import android.provider.ContactsContract
|
import android.provider.ContactsContract
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@ -243,11 +243,17 @@ private fun ContactsScreen(navController: NavController) {
|
|||||||
if (photoBase64.isNotEmpty())
|
if (photoBase64.isNotEmpty())
|
||||||
try {
|
try {
|
||||||
val decodedString = Base64.decode(photoBase64, Base64.DEFAULT)
|
val decodedString = Base64.decode(photoBase64, Base64.DEFAULT)
|
||||||
val decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.size)
|
val decodedByte = Utils.decodeSampledBitmapFromByteArray(decodedString, 192, 192)
|
||||||
if (decodedByte != null) {
|
if (decodedByte != null) {
|
||||||
|
val scaledByte = decodedByte.scale(192, 192)
|
||||||
val avatarFile = File(BaresipService.filesPath, "$contactId.png")
|
val avatarFile = File(BaresipService.filesPath, "$contactId.png")
|
||||||
FileOutputStream(avatarFile).use { out ->
|
try {
|
||||||
decodedByte.compress(Bitmap.CompressFormat.PNG, 100, out)
|
val out = FileOutputStream(avatarFile)
|
||||||
|
scaledByte.compress(Bitmap.CompressFormat.PNG, 100, out)
|
||||||
|
out.flush()
|
||||||
|
out.close()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to save photo for $name: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import android.content.Intent
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.graphics.PorterDuffXfermode
|
import android.graphics.PorterDuffXfermode
|
||||||
@ -1390,6 +1391,57 @@ object Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
|
||||||
|
val (height: Int, width: Int) = options.outHeight to options.outWidth
|
||||||
|
var inSampleSize = 1
|
||||||
|
if (height > reqHeight || width > reqWidth) {
|
||||||
|
val halfHeight = height / 2
|
||||||
|
val halfWidth = width / 2
|
||||||
|
while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth)
|
||||||
|
inSampleSize *= 2
|
||||||
|
}
|
||||||
|
return inSampleSize
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decodeSampledBitmapFromUri(ctx: Context, uri: Uri, reqWidth: Int, reqHeight: Int): Bitmap? {
|
||||||
|
return try {
|
||||||
|
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||||
|
ctx.contentResolver.openInputStream(uri).use { BitmapFactory.decodeStream(it, null, options) }
|
||||||
|
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
|
||||||
|
options.inJustDecodeBounds = false
|
||||||
|
ctx.contentResolver.openInputStream(uri).use { BitmapFactory.decodeStream(it, null, options) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to decode sampled bitmap from URI: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decodeSampledBitmapFromByteArray(data: ByteArray, reqWidth: Int, reqHeight: Int): Bitmap? {
|
||||||
|
return try {
|
||||||
|
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||||
|
BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
||||||
|
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
|
||||||
|
options.inJustDecodeBounds = false
|
||||||
|
BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to decode sampled bitmap from ByteArray: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decodeSampledBitmapFromFile(path: String, reqWidth: Int, reqHeight: Int): Bitmap? {
|
||||||
|
return try {
|
||||||
|
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||||
|
BitmapFactory.decodeFile(path, options)
|
||||||
|
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
|
||||||
|
options.inJustDecodeBounds = false
|
||||||
|
BitmapFactory.decodeFile(path, options)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Failed to decode sampled bitmap from file: ${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresApi(29)
|
@RequiresApi(29)
|
||||||
@SuppressLint("HardwareIds")
|
@SuppressLint("HardwareIds")
|
||||||
fun getLine1Number(ctx: Context, subscriptionId: Int = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID): String? {
|
fun getLine1Number(ctx: Context, subscriptionId: Int = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID): String? {
|
||||||
|
|||||||
Reference in New Issue
Block a user