123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- package io.nexilis.alpha.ui.components
- import android.graphics.BitmapFactory
- import androidx.compose.foundation.Image
- import androidx.compose.foundation.background
- import androidx.compose.foundation.layout.Column
- import androidx.compose.foundation.layout.Row
- import androidx.compose.foundation.layout.aspectRatio
- import androidx.compose.foundation.layout.fillMaxWidth
- import androidx.compose.foundation.layout.padding
- import androidx.compose.foundation.layout.size
- import androidx.compose.foundation.layout.widthIn
- import androidx.compose.foundation.shape.RoundedCornerShape
- import androidx.compose.material.icons.Icons
- import androidx.compose.material.icons.filled.DownloadForOffline
- import androidx.compose.material.icons.filled.Headset
- import androidx.compose.material.icons.filled.PlayArrow
- import androidx.compose.material3.CircularProgressIndicator
- import androidx.compose.material3.Icon
- import androidx.compose.material3.IconButton
- import androidx.compose.material3.LinearProgressIndicator
- import androidx.compose.material3.MaterialTheme
- import androidx.compose.material3.Text
- import androidx.compose.runtime.Composable
- import androidx.compose.runtime.getValue
- import androidx.compose.runtime.livedata.observeAsState
- import androidx.compose.ui.Alignment
- import androidx.compose.ui.Modifier
- import androidx.compose.ui.graphics.Color
- import androidx.compose.ui.graphics.graphicsLayer
- import androidx.compose.ui.graphics.vector.ImageVector
- import androidx.compose.ui.layout.ContentScale
- import androidx.compose.ui.platform.LocalContext
- import androidx.compose.ui.res.vectorResource
- import androidx.compose.ui.text.intl.Locale
- import androidx.compose.ui.text.style.TextOverflow
- import androidx.compose.ui.text.toUpperCase
- import androidx.compose.ui.unit.dp
- import androidx.core.content.FileProvider
- import androidx.exifinterface.media.ExifInterface
- import androidx.hilt.navigation.compose.hiltViewModel
- import coil.compose.rememberAsyncImagePainter
- import io.nexilis.alpha.R
- import io.nexilis.service.core.Orange
- import io.nexilis.service.core.extension
- import io.nexilis.service.core.toHumanStandard
- import io.nexilis.service.data.entities.Message
- import io.nexilis.service.data.viewmodels.ProgressViewModel
- import java.io.File
- import java.io.FileInputStream
- @Composable
- fun ContentChat(modifier: Modifier = Modifier, message: Message) {
- val context = LocalContext.current
- if (message.image_id.isNotEmpty()) {
- Column(modifier = Modifier.padding(2.dp)) {
- val file = File(context.filesDir, message.image_id)
- val uri = FileProvider.getUriForFile(
- context, context.applicationContext.packageName + ".provider", file
- )
- var width: Int
- var height: Int
- FileInputStream(file).use {
- val exif = ExifInterface(it)
- width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 1)
- height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 1)
- val orientation = exif.getAttributeInt(
- ExifInterface.TAG_ORIENTATION,
- ExifInterface.ORIENTATION_UNDEFINED
- )
- if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || orientation == ExifInterface.ORIENTATION_ROTATE_270) {
- height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 1)
- width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 1)
- }
- }
- if (width == 0) {
- FileInputStream(file).use {
- val bitmap = BitmapFactory.decodeStream(it)
- width = bitmap.width
- if (height == 0) height = bitmap.height
- }
- }
- val aspectRatio = width.toFloat() / height.toFloat()
- Image(
- painter = rememberAsyncImagePainter(uri),
- contentDescription = "",
- modifier = modifier
- .aspectRatio(aspectRatio)
- .graphicsLayer {
- shape = RoundedCornerShape(12.dp)
- clip = true
- },
- contentScale = ContentScale.Fit
- )
- if (message.message_text.isNotEmpty()) {
- Text(
- text = message.message_text,
- modifier = Modifier.padding(8.dp),
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyMedium,
- )
- }
- }
- } else if (message.file_id.isNotEmpty()) {
- Column(
- modifier = Modifier
- .padding(2.dp)
- .fillMaxWidth(0.65f)
- ) {
- Row(modifier = Modifier
- .graphicsLayer {
- shape = RoundedCornerShape(12.dp)
- clip = true
- }
- .align(Alignment.CenterHorizontally)
- .background(MaterialTheme.colorScheme.surface)
- .padding(8.dp)
- ) {
- FileType(
- message = message, modifier = Modifier
- .align(Alignment.CenterVertically)
- )
- Column(
- modifier = Modifier
- .weight(1f)
- ) {
- Text(
- text = message.message_text.split("|").first(),
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyMedium,
- maxLines = 2,
- overflow = TextOverflow.Ellipsis
- )
- FileDesc(file = message.file_id)
- }
- FileProgress(
- message = message,
- modifier = Modifier.align(Alignment.CenterVertically)
- )
- }
- val messageText = message.message_text.split("|").last()
- if (messageText.isNotEmpty()) {
- Text(
- text = messageText,
- modifier = Modifier.padding(8.dp),
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyMedium,
- )
- }
- }
- } else if (message.audio_id.isNotEmpty()) {
- Column(
- modifier = Modifier
- .padding(2.dp)
- .widthIn(max = 210.dp)
- ) {
- Row(modifier = Modifier
- .graphicsLayer {
- shape = RoundedCornerShape(12.dp)
- clip = true
- }
- .background(MaterialTheme.colorScheme.surface)) {
- Icon(
- modifier = Modifier
- .size(48.dp)
- .padding(start = 8.dp)
- .align(Alignment.CenterVertically),
- imageVector = Icons.Default.Headset,
- contentDescription = null,
- tint = Color.Orange
- )
- Column {
- Row(
- modifier = Modifier.padding(
- start = 4.dp,
- top = 8.dp,
- end = 8.dp,
- bottom = 8.dp
- )
- ) {
- Icon(
- modifier = Modifier
- .size(32.dp)
- .align(Alignment.CenterVertically),
- imageVector = Icons.Default.PlayArrow,
- contentDescription = null,
- tint = MaterialTheme.colorScheme.primary
- )
- LinearProgressIndicator(
- progress = { 0.5f },
- modifier = Modifier
- .align(Alignment.CenterVertically)
- .fillMaxWidth(),
- )
- }
- Text(
- modifier = Modifier.padding(
- start = 4.dp,
- top = 8.dp,
- end = 8.dp,
- bottom = 8.dp
- ),
- color = MaterialTheme.colorScheme.onSurface,
- text = message.message_text.split("|").first(),
- style = MaterialTheme.typography.labelSmall,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis
- )
- }
- }
- val messageText = message.message_text.split("|").last()
- if (messageText.isNotEmpty()) {
- Text(
- text = messageText,
- modifier = Modifier.padding(8.dp),
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyMedium,
- )
- }
- }
- } else {
- Text(
- text = message.message_text,
- modifier = Modifier.padding(8.dp),
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyMedium,
- )
- }
- }
- @Composable
- fun FileType(message: Message, modifier: Modifier) {
- val imageVector = when (message.file_id.extension) {
- "apk", "aab" -> ImageVector.vectorResource(id = R.drawable.ic_apk)
- "aac", "mp3", "m4a", "ogg" -> ImageVector.vectorResource(id = R.drawable.ic_audio)
- "xls", "xlsx" -> ImageVector.vectorResource(id = R.drawable.ic_excel)
- "jpg", "jpeg", "webp", "png", "svg" -> ImageVector.vectorResource(id = R.drawable.ic_image)
- "pdf" -> ImageVector.vectorResource(id = R.drawable.ic_pdf)
- "ppt", "pptx" -> ImageVector.vectorResource(id = R.drawable.ic_ppt)
- "txt" -> ImageVector.vectorResource(id = R.drawable.ic_txt)
- "mp4", "mov", "3gp" -> ImageVector.vectorResource(id = R.drawable.ic_video)
- "doc", "docx" -> ImageVector.vectorResource(id = R.drawable.ic_word)
- "zip", "gz", "rar" -> ImageVector.vectorResource(id = R.drawable.ic_zip)
- else -> ImageVector.vectorResource(id = R.drawable.ic_file)
- }
- Image(
- modifier = modifier
- .size(48.dp),
- imageVector = imageVector,
- contentDescription = null
- )
- }
- @Composable
- fun FileDesc(file: String, modifier: Modifier = Modifier) {
- val context = LocalContext.current
- val f = File(context.filesDir, file)
- if (f.exists()) {
- Text(
- modifier = modifier,
- text = "${
- f.length().toHumanStandard()
- } • ${file.extension.toUpperCase(Locale.current)}",
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.labelSmall,
- )
- }
- }
- @Composable
- fun FileProgress(message: Message, modifier: Modifier = Modifier) {
- val context = LocalContext.current
- val progressViewModel: ProgressViewModel = hiltViewModel()
- val progress by progressViewModel.get(message.message_id).observeAsState()
- progress?.let {
- if (it.value > 0f && it.value < 1f) {
- CircularProgressIndicator(
- progress = { it.value },
- modifier = modifier
- .size(32.dp),
- )
- }
- }
- val file = File(context.filesDir, message.file_id)
- if (!file.exists()) {
- IconButton(
- onClick = { /*TODO*/ },
- modifier = modifier
- ) {
- Icon(
- imageVector = Icons.Default.DownloadForOffline,
- contentDescription = null,
- tint = Color.LightGray
- )
- }
- }
- }
|