Create Message Details screen

This commit is contained in:
andrew 2023-06-29 11:37:55 +09:30
parent ab8b2c42b9
commit fc108b34db
2 changed files with 166 additions and 61 deletions

View File

@ -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

View File

@ -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)
finish()
} }
} }
fun updateContent() { data class TitledText(val title: String, val value: String)
val dateLocale = Locale.getDefault()
val dateFormatter: SimpleDateFormat = DateUtils.getDetailedDateFormatter(this, dateLocale)
binding.sentTime.text = dateFormatter.format(Date(messageRecord!!.dateSent))
val errorMessage = DatabaseComponent.get(this).lokiMessageDatabase().getErrorMessage(messageRecord!!.getId()) @OptIn(ExperimentalLayoutApi::class)
if (errorMessage != null) { @Preview
binding.errorMessage.text = errorMessage @Composable
binding.resendContainer.isVisible = true fun MessageDetails() {
binding.errorContainer.isVisible = true val fileDetails = listOf(
} else { TitledText("File Id:", "1237896548514214124235985214"),
binding.errorContainer.isVisible = false TitledText("File Type:", ".PNG"),
binding.resendContainer.isVisible = false 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)
}
}
}
} }
}
if (messageRecord!!.expiresIn <= 0 || messageRecord!!.expireStarted <= 0) { @Composable
binding.expiresContainer.visibility = View.GONE fun Divider() {
} else { Divider(modifier = Modifier.padding(horizontal = 16.dp), thickness = 1.dp, color = Color(0xff414141))
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 ItemButton(text: String, @DrawableRes icon: Int, color: Color = Color.White) {
TextButton(
modifier = Modifier
.fillMaxWidth()
.height(60.dp),
onClick = {},
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())
} }
} }
@Composable
fun Card(content: @Composable () -> Unit) {
CardWithPadding(0.dp) { content() }
}
@Composable
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)
}
} }