mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-30 22:55:48 +00:00
android: Different width for image in a message based on orientation (#1227)
* android: Different width for image in a message based on orientation * Remember Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
65f8ad4423
commit
e0582d656e
@@ -538,7 +538,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, provider, showMember = showMember, chatModelIncognito = chatModelIncognito, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall, scrollToItem = scrollToItem)
|
||||
}
|
||||
} else {
|
||||
Box(Modifier.padding(start = 86.dp, end = 12.dp).then(swipeableModifier)) {
|
||||
Box(Modifier.padding(start = 104.dp, end = 12.dp).then(swipeableModifier)) {
|
||||
ChatItemView(user, chat.chatInfo, cItem, composeState, cxt, uriHandler, provider, chatModelIncognito = chatModelIncognito, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall, scrollToItem = scrollToItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
package chat.simplex.app.views.chat.item
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import androidx.compose.foundation.Image
|
||||
@@ -9,8 +11,7 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.outlined.ArrowDownward
|
||||
import androidx.compose.material.icons.outlined.MoreHoriz
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -18,16 +19,16 @@ import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.*
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.FileProvider
|
||||
import chat.simplex.app.BuildConfig
|
||||
import chat.simplex.app.*
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.CIFile
|
||||
import chat.simplex.app.model.CIFileStatus
|
||||
import chat.simplex.app.views.chat.item.ImageFullScreenView
|
||||
import chat.simplex.app.views.chat.item.ImageGalleryProvider
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import coil.ImageLoader
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
@@ -93,6 +94,12 @@ fun CIImageView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun imageViewFullWidth(): Dp {
|
||||
val approximatePadding = 100.dp
|
||||
return with(LocalDensity.current) { minOf(1000.dp, LocalView.current.width.toDp() - approximatePadding) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun imageView(imageBitmap: Bitmap, onClick: () -> Unit) {
|
||||
Image(
|
||||
@@ -101,7 +108,7 @@ fun CIImageView(
|
||||
// .width(1000.dp) is a hack for image to increase IntrinsicSize of FramedItemView
|
||||
// if text is short and take all available width if text is long
|
||||
modifier = Modifier
|
||||
.width(1000.dp)
|
||||
.width(if (imageBitmap.width * 0.97 <= imageBitmap.height) imageViewFullWidth() * 0.75f else 1000.dp)
|
||||
.combinedClickable(
|
||||
onLongClick = { showMenu.value = true },
|
||||
onClick = onClick
|
||||
@@ -118,7 +125,7 @@ fun CIImageView(
|
||||
// .width(1000.dp) is a hack for image to increase IntrinsicSize of FramedItemView
|
||||
// if text is short and take all available width if text is long
|
||||
modifier = Modifier
|
||||
.width(1000.dp)
|
||||
.width(if (painter.intrinsicSize.width * 0.97 <= painter.intrinsicSize.height) imageViewFullWidth() * 0.75f else 1000.dp)
|
||||
.combinedClickable(
|
||||
onLongClick = { showMenu.value = true },
|
||||
onClick = onClick
|
||||
@@ -134,21 +141,20 @@ fun CIImageView(
|
||||
return false
|
||||
}
|
||||
|
||||
Box(contentAlignment = Alignment.TopEnd) {
|
||||
fun imageAndFilePath(file: CIFile?): Pair<Bitmap?, String?> {
|
||||
val imageBitmap: Bitmap? = getLoadedImage(SimplexApp.context, file)
|
||||
val filePath = getLoadedFilePath(SimplexApp.context, file)
|
||||
return imageBitmap to filePath
|
||||
}
|
||||
|
||||
Box(
|
||||
Modifier.layoutId(CHAT_IMAGE_LAYOUT_ID),
|
||||
contentAlignment = Alignment.TopEnd
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val imageBitmap: Bitmap? = getLoadedImage(context, file)
|
||||
val filePath = getLoadedFilePath(context, file)
|
||||
val (imageBitmap, filePath) = remember(file) { imageAndFilePath(file) }
|
||||
if (imageBitmap != null && filePath != null) {
|
||||
val uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath))
|
||||
val imageLoader = ImageLoader.Builder(context)
|
||||
.components {
|
||||
if (SDK_INT >= 28) {
|
||||
add(ImageDecoderDecoder.Factory())
|
||||
} else {
|
||||
add(GifDecoder.Factory())
|
||||
}
|
||||
}
|
||||
.build()
|
||||
val uri = remember(filePath) { FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", File(filePath)) }
|
||||
val imagePainter = rememberAsyncImagePainter(
|
||||
ImageRequest.Builder(context).data(data = uri).size(coil.size.Size.ORIGINAL).build(),
|
||||
placeholder = BitmapPainter(imageBitmap.asImageBitmap()), // show original image while it's still loading by coil
|
||||
@@ -190,3 +196,13 @@ fun CIImageView(
|
||||
loadingIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
private val imageLoader = ImageLoader.Builder(SimplexApp.context)
|
||||
.components {
|
||||
if (SDK_INT >= 28) {
|
||||
add(ImageDecoderDecoder.Factory())
|
||||
} else {
|
||||
add(GifDecoder.Factory())
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package chat.simplex.app.views.chat.item
|
||||
|
||||
import CIImageView
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
@@ -14,13 +13,13 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.*
|
||||
import androidx.compose.ui.platform.UriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.tooling.preview.*
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.compose.ui.util.fastMap
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.*
|
||||
@@ -119,27 +118,24 @@ fun FramedItemView(
|
||||
var metaColor = HighOrLowlight
|
||||
Box(contentAlignment = Alignment.BottomEnd) {
|
||||
Column(Modifier.width(IntrinsicSize.Max)) {
|
||||
val qi = ci.quotedItem
|
||||
if (qi != null) {
|
||||
ciQuoteView(qi)
|
||||
}
|
||||
if (ci.file == null && ci.formattedText == null && isShortEmoji(ci.content.text)) {
|
||||
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(bottom = 2.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
EmojiText(ci.content.text)
|
||||
Text("")
|
||||
PriorityLayout(Modifier, CHAT_IMAGE_LAYOUT_ID) {
|
||||
ci.quotedItem?.let { ciQuoteView(it) }
|
||||
if (ci.file == null && ci.formattedText == null && isShortEmoji(ci.content.text)) {
|
||||
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(bottom = 2.dp)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
EmojiText(ci.content.text)
|
||||
Text("")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
} else {
|
||||
when (val mc = ci.content.msgContent) {
|
||||
is MsgContent.MCImage -> {
|
||||
CIImageView(image = mc.image, file = ci.file, imageProvider ?: return@Box, showMenu, receiveFile)
|
||||
CIImageView(image = mc.image, file = ci.file, imageProvider ?: return@PriorityLayout, showMenu, receiveFile)
|
||||
if (mc.text == "") {
|
||||
metaColor = Color.White
|
||||
} else {
|
||||
@@ -184,6 +180,37 @@ fun CIMarkdownText(
|
||||
}
|
||||
}
|
||||
|
||||
const val CHAT_IMAGE_LAYOUT_ID = "chatImage"
|
||||
|
||||
@Composable
|
||||
fun PriorityLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
priorityLayoutId: String,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
Layout(
|
||||
content = content,
|
||||
modifier = modifier
|
||||
) { measureable, constraints ->
|
||||
// Find important element which should tell what max width other elements can use
|
||||
// Expecting only one such element. Can be less than one but not more
|
||||
val imagePlaceable = measureable.firstOrNull { it.layoutId == priorityLayoutId }?.measure(constraints)
|
||||
val placeables: List<Placeable> = measureable.fastMap {
|
||||
if (it.layoutId == priorityLayoutId)
|
||||
imagePlaceable!!
|
||||
else
|
||||
it.measure(constraints.copy(maxWidth = imagePlaceable?.width ?: constraints.maxWidth)) }
|
||||
// Limit width for every other element to width of important element and height for a sum of all elements
|
||||
layout(imagePlaceable?.measuredWidth ?: placeables.maxOf { it.width }, placeables.sumOf { it.height }) {
|
||||
var y = 0
|
||||
placeables.forEach {
|
||||
it.place(0, y)
|
||||
y += it.measuredHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EditedProvider: PreviewParameterProvider<Boolean> {
|
||||
override val values = listOf(false, true).asSequence()
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
<string name="images_limit_title">Zu viele Bilder!</string>
|
||||
<string name="images_limit_desc">Es können nur 10 Bilder auf einmal gesendet werden</string>
|
||||
|
||||
<!-- Images - CIImageView.kt -->
|
||||
<!-- Images - chat.simplex.app.views.chat.item.CIImageView.kt -->
|
||||
<string name="image_descr">Bild</string>
|
||||
<string name="icon_descr_waiting_for_image">Warten auf ein Bild</string>
|
||||
<string name="icon_descr_asked_to_receive">Um Empfang des Bildes gebeten</string>
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
<string name="images_limit_title">Слишком много изображений!</string>
|
||||
<string name="images_limit_desc">Только 10 изображений могут быть отправлены одномоментно</string>
|
||||
|
||||
<!-- Images - CIImageView.kt -->
|
||||
<!-- Images - chat.simplex.app.views.chat.item.CIImageView.kt -->
|
||||
<string name="image_descr">Изображение</string>
|
||||
<string name="icon_descr_waiting_for_image">Ожидается прием изображения</string>
|
||||
<string name="icon_descr_asked_to_receive">Предложено получить изображение</string>
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
<string name="images_limit_title">Too many images!</string>
|
||||
<string name="images_limit_desc">Only 10 images can be sent at the same time</string>
|
||||
|
||||
<!-- Images - CIImageView.kt -->
|
||||
<!-- Images - chat.simplex.app.views.chat.item.CIImageView.kt -->
|
||||
<string name="image_descr">Image</string>
|
||||
<string name="icon_descr_waiting_for_image">Waiting for image</string>
|
||||
<string name="icon_descr_asked_to_receive">Asked to receive the image</string>
|
||||
|
||||
Reference in New Issue
Block a user