Create Message Details screen

pull/1261/head
andrew 2 years ago
parent ab8b2c42b9
commit fc108b34db

@ -28,6 +28,7 @@ configurations.all {
} }
dependencies { dependencies {
implementation "androidx.appcompat:appcompat:$appcompatVersion" implementation "androidx.appcompat:appcompat:$appcompatVersion"
implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation "com.google.android.material:material:$materialVersion" implementation "com.google.android.material:material:$materialVersion"
@ -161,8 +162,11 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.4' testImplementation 'org.robolectric:shadows-multidex:4.4'
implementation 'androidx.compose.ui:ui:1.4.3' implementation 'androidx.compose.ui:ui:1.4.3'
implementation 'androidx.compose.material:material:1.4.3'
implementation 'androidx.compose.ui:ui-tooling:1.4.3' implementation 'androidx.compose.ui:ui-tooling:1.4.3'
implementation "com.google.accompanist:accompanist-themeadapter-appcompat:0.30.1"
implementation 'androidx.compose.foundation:foundation-layout:1.5.0-alpha02'
implementation 'androidx.compose.material:material:1.5.0-alpha02'
} }
def canonicalVersionCode = 338 def canonicalVersionCode = 338

@ -2,32 +2,49 @@ package org.thoughtcrime.securesms.conversation.v2
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.annotation.DrawableRes
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
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.google.accompanist.themeadapter.appcompat.AppCompatTheme
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityMessageDetailBinding
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.open_groups.OpenGroupApi
import org.session.libsession.messaging.utilities.SessionId
import org.session.libsession.messaging.utilities.SodiumUtilities
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.ExpirationUtil
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.IdPrefix
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.conversation.v2.utilities.ResendMessageUtilities
import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.Storage
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.util.DateUtils
import java.text.SimpleDateFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class MessageDetailActivity: PassphraseRequiredActionBarActivity() { class MessageDetailActivity: PassphraseRequiredActionBarActivity() {
private lateinit var binding: ActivityMessageDetailBinding
var messageRecord: MessageRecord? = null var messageRecord: MessageRecord? = null
@Inject @Inject
@ -42,58 +59,142 @@ class MessageDetailActivity: PassphraseRequiredActionBarActivity() {
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
super.onCreate(savedInstanceState, ready) super.onCreate(savedInstanceState, ready)
binding = ActivityMessageDetailBinding.inflate(layoutInflater)
setContentView(binding.root)
title = resources.getString(R.string.conversation_context__menu_message_details)
val timestamp = intent.getLongExtra(MESSAGE_TIMESTAMP, -1L) val timestamp = intent.getLongExtra(MESSAGE_TIMESTAMP, -1L)
// We only show this screen for messages fail to send,
// so the author of the messages must be the current user. title = resources.getString(R.string.conversation_context__menu_message_details)
val author = Address.fromSerialized(TextSecurePreferences.getLocalNumber(this)!!)
messageRecord = DatabaseComponent.get(this).mmsSmsDatabase().getMessageFor(timestamp, author) ?: run { setContentView(createComposeView())
finish() }
return
} private fun createComposeView(): ComposeView = ComposeView(this).apply {
val threadId = messageRecord!!.threadId id = View.generateViewId()
val openGroup = storage.getOpenGroup(threadId) setContent {
val blindedKey = openGroup?.let { group -> MessageDetails()
val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return@let null
val blindingEnabled = storage.getServerCapabilities(group.server).contains(OpenGroupApi.Capability.BLIND.name.lowercase())
if (blindingEnabled) {
SodiumUtilities.blindedKeyPair(group.publicKey, userEdKeyPair)?.publicKey?.asBytes
?.let { SessionId(IdPrefix.BLINDED, it) }?.hexString
} else null
} }
updateContent() }
binding.resendButton.setOnClickListener {
ResendMessageUtilities.resend(this, messageRecord!!, blindedKey) data class TitledText(val title: String, val value: String)
finish()
@OptIn(ExperimentalLayoutApi::class)
@Preview
@Composable
fun MessageDetails() {
val fileDetails = listOf(
TitledText("File Id:", "1237896548514214124235985214"),
TitledText("File Type:", ".PNG"),
TitledText("File Size:", "6mb"),
TitledText("Resolution:", "550x550"),
TitledText("Duration:", "N/A"),
)
val sent = TitledText("Sent:", "6:12 AM Tue, 09/08/2022 ")
val received = TitledText("Received:", "6:12 AM Tue, 09/08/2022 ")
val user = TitledText("Connor", "d4f1g54sdf5g1d5f4g65ds4564df65f4g65d54gdfsg")
AppCompatTheme {
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(16.dp)) {
CardWithPadding {
FlowRow(
verticalArrangement = Arrangement.spacedBy(16.dp),
maxItemsInEachRow = 2
) {
fileDetails.forEach {
titledText(it, Modifier.weight(1f))
}
}
}
CardWithPadding {
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
titledText(sent)
titledText(received)
titledView("From:") {
Row {
Box(modifier = Modifier.width(60.dp).height(60.dp))
Column {
titledText(user)
}
}
}
}
}
Card {
Column {
ItemButton("Reply", R.drawable.ic_reply)
Divider()
ItemButton("Resend", R.drawable.ic_reply)
Divider()
ItemButton("Delete", R.drawable.ic_delete_24, color = Color.Red)
}
}
}
} }
} }
fun updateContent() { @Composable
val dateLocale = Locale.getDefault() fun Divider() {
val dateFormatter: SimpleDateFormat = DateUtils.getDetailedDateFormatter(this, dateLocale) Divider(modifier = Modifier.padding(horizontal = 16.dp), thickness = 1.dp, color = Color(0xff414141))
binding.sentTime.text = dateFormatter.format(Date(messageRecord!!.dateSent)) }
val errorMessage = DatabaseComponent.get(this).lokiMessageDatabase().getErrorMessage(messageRecord!!.getId()) @Composable
if (errorMessage != null) { fun ItemButton(text: String, @DrawableRes icon: Int, color: Color = Color.White) {
binding.errorMessage.text = errorMessage TextButton(
binding.resendContainer.isVisible = true modifier = Modifier
binding.errorContainer.isVisible = true .fillMaxWidth()
} else { .height(60.dp),
binding.errorContainer.isVisible = false onClick = {},
binding.resendContainer.isVisible = false shape = RectangleShape,
) {
Box(modifier = Modifier.width(80.dp).fillMaxHeight()) {
Icon(
painter = painterResource(id = icon),
contentDescription = "",
tint = color,
modifier = Modifier.align(Alignment.Center)
)
}
Text(text, color = color, modifier = Modifier.fillMaxWidth())
} }
}
if (messageRecord!!.expiresIn <= 0 || messageRecord!!.expireStarted <= 0) { @Composable
binding.expiresContainer.visibility = View.GONE fun Card(content: @Composable () -> Unit) {
} else { CardWithPadding(0.dp) { content() }
binding.expiresContainer.visibility = View.VISIBLE }
val elapsed = SnodeAPI.nowWithOffset - messageRecord!!.expireStarted
val remaining = messageRecord!!.expiresIn - elapsed
val duration = ExpirationUtil.getExpirationDisplayValue(this, Math.max((remaining / 1000).toInt(), 1)) @Composable
binding.expiresIn.text = duration fun CardWithPadding(padding: Dp = 24.dp, content: @Composable () -> Unit) {
Card(
shape = RoundedCornerShape(16.dp),
elevation = 0.dp,
modifier = Modifier
.wrapContentHeight()
.fillMaxWidth()
.padding(horizontal = 32.dp),
backgroundColor = Color(0xff1b1b1b),
contentColor = Color.White
) { Box(Modifier.padding(padding)) { content() } }
}
@Composable
fun titledText(titledText: TitledText, modifier: Modifier = Modifier) {
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
Title(titledText.title)
Text(titledText.value)
}
}
@Composable
fun titledView(title: String, modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
Title(title)
content()
} }
} }
@Composable
fun Title(text: String) {
Text(text, fontWeight = FontWeight.Bold)
}
} }
Loading…
Cancel
Save