mirror of
https://github.com/Steffo99/twom.git
synced 2024-11-21 15:44:26 +00:00
Image refactors and improvements
This commit is contained in:
parent
deb0b8be61
commit
ed5d6a0633
7 changed files with 99 additions and 60 deletions
|
@ -11,7 +11,9 @@ import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 40, heightDp = 40)
|
@Preview(name = "Regular", widthDp = 40, heightDp = 40)
|
||||||
|
@Preview(name = "Double font scale", widthDp = 40, heightDp = 40, fontScale = 2f)
|
||||||
|
@Preview(name = "Quadruple font scale", widthDp = 40, heightDp = 40, fontScale = 4f)
|
||||||
fun AvatarImage(
|
fun AvatarImage(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
bitmap: ImageBitmap? = null,
|
bitmap: ImageBitmap? = null,
|
||||||
|
|
|
@ -16,7 +16,9 @@ import eu.steffo.twom.utils.BitmapUtilities
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 40, heightDp = 40)
|
@Preview(name = "Regular", widthDp = 40, heightDp = 40)
|
||||||
|
@Preview(name = "Double font scale", widthDp = 40, heightDp = 40, fontScale = 2f)
|
||||||
|
@Preview(name = "Quadruple font scale", widthDp = 40, heightDp = 40, fontScale = 4f)
|
||||||
fun AvatarPicker(
|
fun AvatarPicker(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
fallbackText: String = "?",
|
fallbackText: String = "?",
|
||||||
|
@ -31,10 +33,7 @@ fun AvatarPicker(
|
||||||
rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) ImageSelect@{
|
rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) ImageSelect@{
|
||||||
it ?: return@ImageSelect
|
it ?: return@ImageSelect
|
||||||
|
|
||||||
val rawBitmap = BitmapUtilities.getRawBitmap(resolver, it) ?: return@ImageSelect
|
val correctedBitmap = BitmapUtilities.getCorrectedBitmap(resolver, it)
|
||||||
val orientation = BitmapUtilities.getOrientation(resolver, it) ?: return@ImageSelect
|
|
||||||
|
|
||||||
val correctedBitmap = BitmapUtilities.squareAndOrient(rawBitmap, orientation)
|
|
||||||
|
|
||||||
onPick(correctedBitmap)
|
onPick(correctedBitmap)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,14 @@
|
||||||
package eu.steffo.twom.composables.avatar
|
package eu.steffo.twom.composables.avatar
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.composables.matrix.LocalSession
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 40, heightDp = 40)
|
@Preview(name = "Regular", widthDp = 40, heightDp = 40)
|
||||||
|
@Preview(name = "Double font scale", widthDp = 40, heightDp = 40, fontScale = 2f)
|
||||||
|
@Preview(name = "Quadruple font scale", widthDp = 40, heightDp = 40, fontScale = 4f)
|
||||||
fun AvatarURL(
|
fun AvatarURL(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
url: String? = "",
|
url: String? = "",
|
||||||
|
@ -24,49 +16,9 @@ fun AvatarURL(
|
||||||
contentDescription: String = "",
|
contentDescription: String = "",
|
||||||
alpha: Float = 1.0f,
|
alpha: Float = 1.0f,
|
||||||
) {
|
) {
|
||||||
val session = LocalSession.current
|
|
||||||
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
|
|
||||||
|
|
||||||
LaunchedEffect(session, url) GetAvatar@{
|
|
||||||
if (session == null) {
|
|
||||||
Log.d("AvatarURL", "Not doing anything, session is null.")
|
|
||||||
bitmap = null
|
|
||||||
return@GetAvatar
|
|
||||||
}
|
|
||||||
if (url == null) {
|
|
||||||
Log.d("AvatarURL", "URL is null, not downloading anything.")
|
|
||||||
bitmap = null
|
|
||||||
return@GetAvatar
|
|
||||||
}
|
|
||||||
if (url.isEmpty()) {
|
|
||||||
Log.d("AvatarURL", "URL is a zero-length string, not downloading anything.")
|
|
||||||
bitmap = null
|
|
||||||
return@GetAvatar
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d("AvatarURL", "Downloading avatar at: $url")
|
|
||||||
lateinit var avatarFile: File
|
|
||||||
try {
|
|
||||||
avatarFile = session.fileService().downloadFile(
|
|
||||||
fileName = "avatar",
|
|
||||||
url = url,
|
|
||||||
mimeType = null,
|
|
||||||
elementToDecrypt = null,
|
|
||||||
)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
Log.e("AvatarURL", "Unable to download avatar at: $url", e)
|
|
||||||
return@GetAvatar
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Should I check the MIME type? And the size of the image?
|
|
||||||
// FIXME: I feel this might be a race condition...
|
|
||||||
Log.d("AvatarURL", "File for $url is: $avatarFile")
|
|
||||||
bitmap = BitmapFactory.decodeFile(avatarFile.absolutePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
AvatarImage(
|
AvatarImage(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
bitmap = bitmap?.asImageBitmap(),
|
bitmap = bitmapFromMatrixFile(url)?.asImageBitmap(),
|
||||||
fallbackText = fallbackText,
|
fallbackText = fallbackText,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
alpha = alpha,
|
alpha = alpha,
|
||||||
|
|
|
@ -7,7 +7,9 @@ import org.matrix.android.sdk.api.session.user.model.User
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 40, heightDp = 40)
|
@Preview(name = "Regular", widthDp = 40, heightDp = 40)
|
||||||
|
@Preview(name = "Double font scale", widthDp = 40, heightDp = 40, fontScale = 2f)
|
||||||
|
@Preview(name = "Quadruple font scale", widthDp = 40, heightDp = 40, fontScale = 4f)
|
||||||
fun AvatarUser(
|
fun AvatarUser(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
user: User? = null,
|
user: User? = null,
|
||||||
|
|
|
@ -12,7 +12,9 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import eu.steffo.twom.composables.matrix.LocalSession
|
import eu.steffo.twom.composables.matrix.LocalSession
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(widthDp = 40, heightDp = 40)
|
@Preview(name = "Regular", widthDp = 40, heightDp = 40)
|
||||||
|
@Preview(name = "Double font scale", widthDp = 40, heightDp = 40, fontScale = 2f)
|
||||||
|
@Preview(name = "Quadruple font scale", widthDp = 40, heightDp = 40, fontScale = 4f)
|
||||||
fun AvatarUserId(
|
fun AvatarUserId(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
userId: String = "",
|
userId: String = "",
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package eu.steffo.twom.composables.avatar
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import eu.steffo.twom.composables.matrix.LocalSession
|
||||||
|
import eu.steffo.twom.utils.BitmapUtilities
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import org.matrix.android.sdk.api.util.md5
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
const val TAG = "bitmapFromMatrixFile"
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun bitmapFromMatrixFile(url: String? = null): Bitmap? {
|
||||||
|
val session = LocalSession.current
|
||||||
|
val resolver = LocalContext.current.contentResolver
|
||||||
|
|
||||||
|
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||||
|
|
||||||
|
Log.v(TAG, "Compositing: $url")
|
||||||
|
|
||||||
|
// LaunchedEffect does not behave as expected: sometimes, the effect is cancelled, but not relaunched!
|
||||||
|
LaunchedEffect(session, url) Fetch@{
|
||||||
|
Log.v(TAG, "Launching: $url")
|
||||||
|
|
||||||
|
if (session == null) {
|
||||||
|
Log.d(TAG, "Session is null, clearing bitmap.")
|
||||||
|
bitmap = null
|
||||||
|
return@Fetch
|
||||||
|
}
|
||||||
|
if (url == null) {
|
||||||
|
Log.d(TAG, "URL is null, clearing bitmap.")
|
||||||
|
bitmap = null
|
||||||
|
return@Fetch
|
||||||
|
}
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
Log.d(TAG, "URL is a zero-length string, clearing bitmap.")
|
||||||
|
bitmap = null
|
||||||
|
return@Fetch
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "Downloading file at: $url")
|
||||||
|
lateinit var avatarFile: File
|
||||||
|
try {
|
||||||
|
avatarFile = session.fileService().downloadFile(
|
||||||
|
fileName = url.md5(),
|
||||||
|
url = url,
|
||||||
|
mimeType = null,
|
||||||
|
elementToDecrypt = null,
|
||||||
|
)
|
||||||
|
} catch (e: CancellationException) {
|
||||||
|
// This makes sure no corrupt image is displayed, at least
|
||||||
|
Log.i(TAG, "Cancelled download of file at: $url", e)
|
||||||
|
bitmap = null
|
||||||
|
return@Fetch
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Log.e(TAG, "Unable to download file at: $url", e)
|
||||||
|
bitmap = null
|
||||||
|
return@Fetch
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "File for $url is: $avatarFile")
|
||||||
|
|
||||||
|
bitmap = BitmapUtilities.getCorrectedBitmap(resolver, avatarFile.toUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap
|
||||||
|
}
|
|
@ -106,6 +106,12 @@ class BitmapUtilities {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCorrectedBitmap(resolver: ContentResolver, uri: Uri): Bitmap? {
|
||||||
|
val rawBitmap = getRawBitmap(resolver, uri) ?: return null
|
||||||
|
val orientation = getOrientation(resolver, uri) ?: return null
|
||||||
|
return squareAndOrient(rawBitmap, orientation)
|
||||||
|
}
|
||||||
|
|
||||||
fun bitmapToCache(id: String, bitmap: Bitmap): File {
|
fun bitmapToCache(id: String, bitmap: Bitmap): File {
|
||||||
val file = File.createTempFile("bitmap_$id", ".jpg")
|
val file = File.createTempFile("bitmap_$id", ".jpg")
|
||||||
file.outputStream().use {
|
file.outputStream().use {
|
||||||
|
|
Loading…
Reference in a new issue