2021-07-13 08:22:10 +02:00
|
|
|
package org.thoughtcrime.securesms.conversation.v2
|
|
|
|
|
2023-06-30 02:18:48 +02:00
|
|
|
import android.content.Intent
|
2021-07-13 08:22:10 +02:00
|
|
|
import android.os.Bundle
|
2023-06-30 15:19:23 +02:00
|
|
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
2023-06-29 04:07:55 +02:00
|
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
|
|
import androidx.compose.foundation.layout.Box
|
|
|
|
import androidx.compose.foundation.layout.Column
|
|
|
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
|
|
import androidx.compose.foundation.layout.FlowRow
|
|
|
|
import androidx.compose.foundation.layout.Row
|
2023-07-03 03:05:50 +02:00
|
|
|
import androidx.compose.foundation.layout.aspectRatio
|
2023-06-29 04:07:55 +02:00
|
|
|
import androidx.compose.foundation.layout.height
|
|
|
|
import androidx.compose.foundation.layout.padding
|
|
|
|
import androidx.compose.foundation.layout.width
|
2023-06-30 15:19:23 +02:00
|
|
|
import androidx.compose.foundation.pager.HorizontalPager
|
2023-07-03 03:05:50 +02:00
|
|
|
import androidx.compose.foundation.pager.rememberPagerState
|
2023-06-29 04:07:55 +02:00
|
|
|
import androidx.compose.foundation.rememberScrollState
|
|
|
|
import androidx.compose.foundation.verticalScroll
|
2023-07-03 05:30:11 +02:00
|
|
|
import androidx.compose.material.Card
|
2023-06-29 04:07:55 +02:00
|
|
|
import androidx.compose.material.Divider
|
2023-06-29 11:44:47 +02:00
|
|
|
import androidx.compose.material.LocalTextStyle
|
2023-06-29 04:07:55 +02:00
|
|
|
import androidx.compose.material.Text
|
|
|
|
import androidx.compose.runtime.Composable
|
2023-06-29 14:41:15 +02:00
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.livedata.observeAsState
|
2023-06-30 03:37:27 +02:00
|
|
|
import androidx.compose.ui.Alignment
|
2023-06-29 04:07:55 +02:00
|
|
|
import androidx.compose.ui.Modifier
|
2023-07-01 10:08:58 +02:00
|
|
|
import androidx.compose.ui.layout.ContentScale
|
2023-06-29 04:07:55 +02:00
|
|
|
import androidx.compose.ui.platform.ComposeView
|
2023-06-29 11:44:47 +02:00
|
|
|
import androidx.compose.ui.text.TextStyle
|
|
|
|
import androidx.compose.ui.text.font.FontFamily
|
2023-06-29 04:07:55 +02:00
|
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
|
|
import androidx.compose.ui.tooling.preview.Preview
|
|
|
|
import androidx.compose.ui.unit.dp
|
2023-06-30 03:31:57 +02:00
|
|
|
import androidx.compose.ui.viewinterop.AndroidView
|
2023-06-29 14:41:15 +02:00
|
|
|
import androidx.lifecycle.LiveData
|
|
|
|
import androidx.lifecycle.MutableLiveData
|
|
|
|
import androidx.lifecycle.ViewModel
|
2023-07-01 10:08:58 +02:00
|
|
|
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
|
|
|
|
import com.bumptech.glide.integration.compose.GlideImage
|
2022-09-26 03:44:59 +02:00
|
|
|
import dagger.hilt.android.AndroidEntryPoint
|
2021-07-13 08:22:10 +02:00
|
|
|
import network.loki.messenger.R
|
2023-07-01 10:08:58 +02:00
|
|
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
|
|
|
import org.session.libsession.utilities.Util
|
2023-06-30 04:05:22 +02:00
|
|
|
import org.session.libsession.utilities.recipients.Recipient
|
2021-07-13 08:22:10 +02:00
|
|
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
2023-06-30 03:37:27 +02:00
|
|
|
import org.thoughtcrime.securesms.components.ProfilePictureView
|
2023-07-01 10:08:58 +02:00
|
|
|
import org.thoughtcrime.securesms.database.AttachmentDatabase
|
2022-09-26 03:44:59 +02:00
|
|
|
import org.thoughtcrime.securesms.database.Storage
|
2021-07-13 08:22:10 +02:00
|
|
|
import org.thoughtcrime.securesms.database.model.MessageRecord
|
2023-06-30 15:19:23 +02:00
|
|
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
2023-06-30 02:18:48 +02:00
|
|
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
2023-07-01 10:08:58 +02:00
|
|
|
import org.thoughtcrime.securesms.mms.Slide
|
2023-06-29 09:41:11 +02:00
|
|
|
import org.thoughtcrime.securesms.ui.AppTheme
|
|
|
|
import org.thoughtcrime.securesms.ui.Cell
|
2023-07-03 05:30:11 +02:00
|
|
|
import org.thoughtcrime.securesms.ui.CellNoMargin
|
|
|
|
import org.thoughtcrime.securesms.ui.CellWithPaddingAndMargin
|
2023-06-29 09:41:11 +02:00
|
|
|
import org.thoughtcrime.securesms.ui.ItemButton
|
|
|
|
import org.thoughtcrime.securesms.ui.LocalExtraColors
|
2023-07-03 05:30:11 +02:00
|
|
|
import org.thoughtcrime.securesms.ui.SessionHorizontalPagerIndicator
|
2023-06-30 05:55:16 +02:00
|
|
|
import org.thoughtcrime.securesms.ui.colorDestructive
|
2023-06-29 09:41:11 +02:00
|
|
|
import org.thoughtcrime.securesms.ui.destructiveButtonColors
|
2022-12-19 01:29:05 +01:00
|
|
|
import java.util.*
|
2023-07-01 10:08:58 +02:00
|
|
|
import java.util.concurrent.TimeUnit
|
2022-09-26 03:44:59 +02:00
|
|
|
import javax.inject.Inject
|
2021-07-13 08:22:10 +02:00
|
|
|
|
2023-06-29 09:41:11 +02:00
|
|
|
|
2022-09-26 03:44:59 +02:00
|
|
|
@AndroidEntryPoint
|
2021-07-13 08:22:10 +02:00
|
|
|
class MessageDetailActivity: PassphraseRequiredActionBarActivity() {
|
2023-06-30 02:18:48 +02:00
|
|
|
|
|
|
|
private var timestamp: Long = 0L
|
|
|
|
|
2021-07-13 08:22:10 +02:00
|
|
|
var messageRecord: MessageRecord? = null
|
|
|
|
|
2022-09-26 03:44:59 +02:00
|
|
|
@Inject
|
|
|
|
lateinit var storage: Storage
|
|
|
|
|
2023-07-01 10:08:58 +02:00
|
|
|
|
2021-07-13 08:22:10 +02:00
|
|
|
companion object {
|
|
|
|
// Extras
|
|
|
|
const val MESSAGE_TIMESTAMP = "message_timestamp"
|
2023-06-30 02:18:48 +02:00
|
|
|
|
|
|
|
const val ON_REPLY = 1
|
|
|
|
const val ON_RESEND = 2
|
|
|
|
const val ON_DELETE = 3
|
|
|
|
}
|
|
|
|
|
|
|
|
val viewModel = MessageDetailsViewModel()
|
|
|
|
|
|
|
|
class MessageDetailsViewModel: ViewModel() {
|
2023-07-01 10:08:58 +02:00
|
|
|
@Inject
|
|
|
|
lateinit var attachmentDb: AttachmentDatabase
|
2023-06-30 02:18:48 +02:00
|
|
|
|
2023-06-30 05:55:16 +02:00
|
|
|
fun setMessageRecord(value: MessageRecord?, error: String?) {
|
2023-06-30 15:19:23 +02:00
|
|
|
val mmsRecord = value as? MmsMessageRecord
|
|
|
|
|
2023-07-01 10:08:58 +02:00
|
|
|
val slides: List<Slide> = mmsRecord?.slideDeck?.thumbnailSlides?.toList() ?: emptyList()
|
2023-06-30 15:19:23 +02:00
|
|
|
|
2023-06-30 02:18:48 +02:00
|
|
|
_details.value = value?.run {
|
|
|
|
MessageDetails(
|
2023-07-01 10:08:58 +02:00
|
|
|
attachments = slides.map { slide ->
|
|
|
|
val duration = slide.takeIf { it.hasAudio() }
|
|
|
|
?.let { it.asAttachment() as? DatabaseAttachment }
|
|
|
|
?.let { attachment ->
|
|
|
|
attachmentDb.getAttachmentAudioExtras(attachment.attachmentId)?.let { audioExtras ->
|
|
|
|
audioExtras.durationMs.takeIf { it > 0 }?.let {
|
|
|
|
String.format("%01d:%02d",
|
|
|
|
TimeUnit.MILLISECONDS.toMinutes(it),
|
|
|
|
TimeUnit.MILLISECONDS.toSeconds(it) % 60)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
val details = slide.run {
|
|
|
|
listOfNotNull(
|
|
|
|
fileName.orNull()?.let { TitledText("File Id:", it) },
|
|
|
|
TitledText("File Type:", asAttachment().contentType),
|
|
|
|
TitledText("File Size:", Util.getPrettyFileSize(fileSize)),
|
|
|
|
if (slide.hasImage()) { TitledText("Resolution:", slide.asAttachment().run { "${width}x$height" } ) } else null,
|
|
|
|
duration?.let { TitledText("Duration:", it) },
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Attachment(slide, details)
|
|
|
|
},
|
2023-06-30 02:18:48 +02:00
|
|
|
sent = dateSent.let(::Date).toString().let { TitledText("Sent:", it) },
|
|
|
|
received = dateReceived.let(::Date).toString().let { TitledText("Received:", it) },
|
2023-06-30 05:55:16 +02:00
|
|
|
error = error?.let { TitledText("Error:", it) },
|
2023-06-30 04:05:22 +02:00
|
|
|
senderInfo = individualRecipient.run { name?.let { TitledText(it, address.serialize()) } },
|
|
|
|
sender = individualRecipient
|
2023-06-30 02:18:48 +02:00
|
|
|
)
|
2023-07-01 10:08:58 +02:00
|
|
|
}
|
2023-06-30 02:18:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private var _details = MutableLiveData(MessageDetails())
|
|
|
|
val details: LiveData<MessageDetails> = _details
|
2021-07-13 08:22:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
|
|
|
super.onCreate(savedInstanceState, ready)
|
2023-06-29 04:07:55 +02:00
|
|
|
|
2023-06-30 02:18:48 +02:00
|
|
|
timestamp = intent.getLongExtra(MESSAGE_TIMESTAMP, -1L)
|
|
|
|
|
2023-06-30 03:03:56 +02:00
|
|
|
messageRecord = DatabaseComponent.get(this).mmsSmsDatabase().getMessageForTimestamp(timestamp) ?: run {
|
2023-06-30 02:18:48 +02:00
|
|
|
finish()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-06-30 05:55:16 +02:00
|
|
|
val error = DatabaseComponent.get(this).lokiMessageDatabase().getErrorMessage(messageRecord!!.getId())
|
|
|
|
|
|
|
|
viewModel.setMessageRecord(messageRecord, error)
|
2023-06-29 04:07:55 +02:00
|
|
|
|
|
|
|
title = resources.getString(R.string.conversation_context__menu_message_details)
|
|
|
|
|
2023-06-29 14:41:15 +02:00
|
|
|
setContentView(ComposeView(this).apply {
|
|
|
|
setContent {
|
|
|
|
MessageDetailsScreen()
|
|
|
|
}
|
|
|
|
})
|
2023-06-29 04:07:55 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 14:41:15 +02:00
|
|
|
@Composable
|
2023-06-30 02:18:48 +02:00
|
|
|
private fun MessageDetailsScreen() {
|
2023-06-29 14:41:15 +02:00
|
|
|
val details by viewModel.details.observeAsState(MessageDetails())
|
2023-06-30 02:18:48 +02:00
|
|
|
MessageDetails(
|
|
|
|
details,
|
|
|
|
onReply = { setResultAndFinish(ON_REPLY) },
|
|
|
|
onResend = { setResultAndFinish(ON_RESEND) },
|
|
|
|
onDelete = { setResultAndFinish(ON_DELETE) }
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun setResultAndFinish(code: Int) {
|
|
|
|
Bundle().apply { putLong(MESSAGE_TIMESTAMP, timestamp) }
|
|
|
|
.let(Intent()::putExtras)
|
2023-06-30 15:19:23 +02:00
|
|
|
.let { setResult(code, it) }
|
2023-06-30 02:18:48 +02:00
|
|
|
|
|
|
|
finish()
|
2023-06-29 04:07:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
data class TitledText(val title: String, val value: String)
|
|
|
|
|
2023-06-29 14:41:15 +02:00
|
|
|
data class MessageDetails(
|
2023-07-01 10:08:58 +02:00
|
|
|
val attachments: List<Attachment> = emptyList(),
|
2023-06-29 14:41:15 +02:00
|
|
|
val sent: TitledText? = null,
|
|
|
|
val received: TitledText? = null,
|
2023-06-30 05:55:16 +02:00
|
|
|
val error: TitledText? = null,
|
2023-06-30 04:05:22 +02:00
|
|
|
val senderInfo: TitledText? = null,
|
|
|
|
val sender: Recipient? = null
|
2023-06-29 14:41:15 +02:00
|
|
|
)
|
|
|
|
|
2023-07-01 10:08:58 +02:00
|
|
|
data class Attachment(
|
|
|
|
val slide: Slide,
|
|
|
|
val fileDetails: List<TitledText>
|
|
|
|
)
|
|
|
|
|
2023-06-29 04:07:55 +02:00
|
|
|
@Preview
|
|
|
|
@Composable
|
2023-06-29 14:41:15 +02:00
|
|
|
fun PreviewMessageDetails() {
|
|
|
|
MessageDetails(
|
2023-06-30 05:55:16 +02:00
|
|
|
MessageDetails(
|
2023-07-01 10:08:58 +02:00
|
|
|
attachments = listOf(),
|
2023-06-30 05:55:16 +02:00
|
|
|
sent = TitledText("Sent:", "6:12 AM Tue, 09/08/2022"),
|
|
|
|
received = TitledText("Received:", "6:12 AM Tue, 09/08/2022"),
|
|
|
|
error = TitledText("Error:", "Message failed to send"),
|
2023-07-01 10:08:58 +02:00
|
|
|
senderInfo = TitledText("Connor", "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54gdfsg"),
|
2023-06-30 05:55:16 +02:00
|
|
|
)
|
2023-06-29 04:07:55 +02:00
|
|
|
)
|
2023-06-29 14:41:15 +02:00
|
|
|
}
|
2023-06-29 04:07:55 +02:00
|
|
|
|
2023-06-29 14:41:15 +02:00
|
|
|
@Composable
|
2023-06-30 02:18:48 +02:00
|
|
|
fun MessageDetails(
|
|
|
|
messageDetails: MessageDetails,
|
|
|
|
onReply: () -> Unit = {},
|
|
|
|
onResend: () -> Unit = {},
|
|
|
|
onDelete: () -> Unit = {},
|
|
|
|
) {
|
2023-06-29 14:41:15 +02:00
|
|
|
messageDetails.apply {
|
|
|
|
AppTheme {
|
|
|
|
Column(
|
|
|
|
modifier = Modifier.verticalScroll(rememberScrollState()),
|
|
|
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
|
|
|
) {
|
2023-07-03 03:05:50 +02:00
|
|
|
Attachments(attachments)
|
2023-07-03 05:30:11 +02:00
|
|
|
if (sent != null || received != null || senderInfo != null) CellWithPaddingAndMargin {
|
2023-06-29 14:41:15 +02:00
|
|
|
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
|
|
|
sent?.let { titledText(it) }
|
|
|
|
received?.let { titledText(it) }
|
2023-06-30 05:55:16 +02:00
|
|
|
error?.let { titledText(it, valueStyle = LocalTextStyle.current.copy(color = colorDestructive)) }
|
2023-06-30 04:05:22 +02:00
|
|
|
senderInfo?.let {
|
2023-06-29 14:41:15 +02:00
|
|
|
titledView("From:") {
|
|
|
|
Row {
|
2023-06-30 05:55:16 +02:00
|
|
|
sender?.let {
|
2023-06-30 15:19:23 +02:00
|
|
|
Box(modifier = Modifier
|
|
|
|
.width(60.dp)
|
|
|
|
.align(Alignment.CenterVertically)) {
|
2023-06-30 05:55:16 +02:00
|
|
|
AndroidView(
|
|
|
|
factory = { ProfilePictureView(it).apply { update(sender) } },
|
|
|
|
modifier = Modifier
|
|
|
|
.width(46.dp)
|
|
|
|
.height(46.dp)
|
|
|
|
)
|
|
|
|
}
|
2023-06-30 03:31:57 +02:00
|
|
|
}
|
2023-06-29 14:41:15 +02:00
|
|
|
Column {
|
|
|
|
titledText(it, valueStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-06-29 04:07:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-06-29 14:41:15 +02:00
|
|
|
Cell {
|
|
|
|
Column {
|
2023-06-30 02:18:48 +02:00
|
|
|
ItemButton("Reply", R.drawable.ic_message_details__reply, onClick = onReply)
|
2023-06-29 14:41:15 +02:00
|
|
|
Divider()
|
2023-06-30 05:55:16 +02:00
|
|
|
if (error != null) {
|
|
|
|
ItemButton("Resend", R.drawable.ic_message_details__refresh, onClick = onResend)
|
|
|
|
Divider()
|
|
|
|
}
|
|
|
|
ItemButton("Delete", R.drawable.ic_message_details__trash, colors = destructiveButtonColors(), onClick = onDelete)
|
2023-06-29 14:41:15 +02:00
|
|
|
}
|
2023-06-29 04:07:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-13 08:22:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-03 03:05:50 +02:00
|
|
|
@Composable
|
|
|
|
fun Attachments(attachments: List<Attachment>) {
|
|
|
|
val slide = attachments.firstOrNull()?.slide ?: return
|
|
|
|
when {
|
|
|
|
slide.hasImage() -> ImageAttachments(attachments)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@OptIn(
|
|
|
|
ExperimentalFoundationApi::class,
|
|
|
|
ExperimentalGlideComposeApi::class,
|
|
|
|
ExperimentalLayoutApi::class,
|
|
|
|
)
|
|
|
|
@Composable
|
|
|
|
fun ImageAttachments(attachments: List<Attachment>) {
|
2023-07-03 05:30:11 +02:00
|
|
|
val imageAttachments = attachments.filter { it.slide.hasImage() }
|
|
|
|
val pagerState = rememberPagerState {
|
|
|
|
imageAttachments.size
|
|
|
|
}
|
2023-07-03 03:05:50 +02:00
|
|
|
|
|
|
|
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
2023-07-03 05:30:11 +02:00
|
|
|
CellNoMargin {
|
|
|
|
Box {
|
|
|
|
HorizontalPager(state = pagerState) { i ->
|
|
|
|
imageAttachments[i].slide.apply {
|
|
|
|
GlideImage(
|
|
|
|
contentScale = ContentScale.Crop,
|
|
|
|
modifier = Modifier.aspectRatio(1f),
|
|
|
|
model = uri,
|
|
|
|
contentDescription = fileName.orNull() ?: "image"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (imageAttachments.size >= 2) {
|
|
|
|
SessionHorizontalPagerIndicator(
|
|
|
|
modifier = Modifier.align(Alignment.BottomCenter),
|
|
|
|
pagerState = pagerState,
|
|
|
|
pageCount = imageAttachments.size,
|
2023-07-03 03:05:50 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
attachments[pagerState.currentPage].fileDetails.takeIf { it.isNotEmpty() }?.let {
|
2023-07-03 05:30:11 +02:00
|
|
|
CellWithPaddingAndMargin {
|
2023-07-03 03:05:50 +02:00
|
|
|
FlowRow(
|
|
|
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
|
|
|
maxItemsInEachRow = 2
|
|
|
|
) {
|
|
|
|
it.forEach { titledText(it, Modifier.weight(1f)) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-29 04:07:55 +02:00
|
|
|
@Composable
|
|
|
|
fun Divider() {
|
2023-06-29 09:41:11 +02:00
|
|
|
Divider(modifier = Modifier.padding(horizontal = 16.dp), thickness = 1.dp, color = LocalExtraColors.current.divider)
|
2023-06-29 04:07:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
2023-06-29 11:44:47 +02:00
|
|
|
fun titledText(titledText: TitledText, modifier: Modifier = Modifier, valueStyle: TextStyle = LocalTextStyle.current) {
|
2023-06-29 04:07:55 +02:00
|
|
|
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
|
|
Title(titledText.title)
|
2023-06-29 11:44:47 +02:00
|
|
|
Text(titledText.value, style = valueStyle)
|
2023-06-29 04:07:55 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-29 09:41:11 +02:00
|
|
|
|
2023-06-29 04:07:55 +02:00
|
|
|
@Composable
|
|
|
|
fun titledView(title: String, modifier: Modifier = Modifier, content: @Composable () -> Unit) {
|
|
|
|
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
|
|
|
Title(title)
|
|
|
|
content()
|
2021-07-13 08:22:10 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-29 04:07:55 +02:00
|
|
|
|
|
|
|
@Composable
|
|
|
|
fun Title(text: String) {
|
|
|
|
Text(text, fontWeight = FontWeight.Bold)
|
|
|
|
}
|
2023-06-29 09:41:11 +02:00
|
|
|
}
|