This commit is contained in:
andrew 2023-07-04 22:10:48 +09:30
parent d6b1440217
commit 8d66d948ca
7 changed files with 112 additions and 45 deletions

View File

@ -1,3 +1,4 @@
buildscript { buildscript {
repositories { repositories {
google() google()
@ -13,6 +14,11 @@ buildscript {
} }
} }
plugins {
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
}
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'witness' apply plugin: 'witness'
@ -29,6 +35,9 @@ configurations.all {
dependencies { dependencies {
implementation("com.google.dagger:hilt-android:2.46.1")
kapt("com.google.dagger:hilt-android-compiler:2.44")
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"
@ -41,7 +50,6 @@ dependencies {
implementation 'androidx.exifinterface:exifinterface:1.3.4' implementation 'androidx.exifinterface:exifinterface:1.3.4'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion"
implementation "androidx.lifecycle:lifecycle-process:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-process:$lifecycleVersion"
@ -328,3 +336,8 @@ def autoResConfig() {
.collect { matcher -> matcher.group(1) } .collect { matcher -> matcher.group(1) }
.sort() .sort()
} }
// Allow references to generated code
kapt {
correctErrorTypes = true
}

View File

@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.conversation.v2
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import androidx.activity.viewModels
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -39,16 +41,24 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
import com.bumptech.glide.integration.compose.GlideImage import com.bumptech.glide.integration.compose.GlideImage
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ViewVisibleMessageBinding
import network.loki.messenger.databinding.ViewVisibleMessageContentBinding
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.MediaPreviewActivity import org.thoughtcrime.securesms.MediaPreviewActivity
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.components.ProfilePictureView import org.thoughtcrime.securesms.components.ProfilePictureView
import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.database.Storage
import org.thoughtcrime.securesms.dependencies.DatabaseComponent import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.mms.ImageSlide
import org.thoughtcrime.securesms.mms.Slide import org.thoughtcrime.securesms.mms.Slide
import org.thoughtcrime.securesms.ui.AppTheme import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.CarouselNextButton import org.thoughtcrime.securesms.ui.CarouselNextButton
@ -64,7 +74,6 @@ import org.thoughtcrime.securesms.ui.colorDestructive
import org.thoughtcrime.securesms.ui.destructiveButtonColors import org.thoughtcrime.securesms.ui.destructiveButtonColors
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class MessageDetailActivity : PassphraseRequiredActionBarActivity() { class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
@ -73,6 +82,8 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
@Inject @Inject
lateinit var storage: Storage lateinit var storage: Storage
private val viewModel: MessageDetailsViewModel by viewModels()
companion object { companion object {
// Extras // Extras
const val MESSAGE_TIMESTAMP = "message_timestamp" const val MESSAGE_TIMESTAMP = "message_timestamp"
@ -82,8 +93,6 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
const val ON_DELETE = 3 const val ON_DELETE = 3
} }
val viewModel = MessageDetailsViewModel()
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) { override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
super.onCreate(savedInstanceState, ready) super.onCreate(savedInstanceState, ready)
@ -116,7 +125,12 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
onResend = { setResultAndFinish(ON_RESEND) }, onResend = { setResultAndFinish(ON_RESEND) },
onDelete = { setResultAndFinish(ON_DELETE) }, onDelete = { setResultAndFinish(ON_DELETE) },
onClickImage = { slide -> onClickImage = { slide ->
MediaPreviewActivity.getPreviewIntent(this, slide, details.mmsRecord, details.sender) MediaPreviewActivity.getPreviewIntent(
this,
slide,
details.mmsRecord,
details.sender
)
.let(::startActivity) .let(::startActivity)
} }
) )
@ -144,6 +158,12 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
) )
} }
private fun onAttachmentNeedsDownload(attachmentId: Long, mmsId: Long) {
lifecycleScope.launch(Dispatchers.IO) {
JobQueue.shared.add(AttachmentDownloadJob(attachmentId, mmsId))
}
}
@Composable @Composable
fun MessageDetails( fun MessageDetails(
messageDetails: MessageDetails, messageDetails: MessageDetails,
@ -152,27 +172,39 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
onDelete: () -> Unit = {}, onDelete: () -> Unit = {},
onClickImage: (Slide) -> Unit = {}, onClickImage: (Slide) -> Unit = {},
) { ) {
messageDetails.apply { AppTheme {
AppTheme { Column(
Column( modifier = Modifier.verticalScroll(rememberScrollState()),
modifier = Modifier.verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(16.dp)
verticalArrangement = Arrangement.spacedBy(16.dp) ) {
) { messageDetails.mmsRecord?.let { message ->
Attachments(attachments) { onClickImage(it) } AndroidView(
MetaDataCell(messageDetails) modifier = Modifier.padding(32.dp),
Buttons( factory = {
error != null, ViewVisibleMessageContentBinding.inflate(LayoutInflater.from(it)).mainContainerConstraint.apply {
onReply, bind(
onResend, message,
onDelete, thread = message.individualRecipient,
onAttachmentNeedsDownload = ::onAttachmentNeedsDownload
)
}
}
) )
} }
Attachments(messageDetails.attachments) { onClickImage(it) }
MetadataCell(messageDetails)
Buttons(
messageDetails.error != null,
onReply,
onResend,
onDelete,
)
} }
} }
} }
@Composable @Composable
fun MetaDataCell( fun MetadataCell(
messageDetails: MessageDetails, messageDetails: MessageDetails,
) { ) {
messageDetails.apply { messageDetails.apply {
@ -205,7 +237,9 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
factory = { factory = {
ProfilePictureView(it).apply { update(sender) } ProfilePictureView(it).apply { update(sender) }
}, },
modifier = Modifier.width(46.dp).height(46.dp) modifier = Modifier
.width(46.dp)
.height(46.dp)
) )
} }
} }
@ -245,13 +279,12 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
@Composable @Composable
fun Attachments(attachments: List<Attachment>, onClick: (Slide) -> Unit) { fun Attachments(attachments: List<Attachment>, onClick: (Slide) -> Unit) {
val slide = attachments.firstOrNull()?.slide ?: return when(attachments.firstOrNull()?.slide) {
when { is ImageSlide -> Carousel(attachments, onClick)
slide.hasImage() -> Carousel(attachments, onClick)
} }
} }
@OptIn(ExperimentalFoundationApi::class,) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun Carousel(attachments: List<Attachment>, onClick: (Slide) -> Unit) { fun Carousel(attachments: List<Attachment>, onClick: (Slide) -> Unit) {
val imageAttachments = attachments.filter { it.slide.hasImage() } val imageAttachments = attachments.filter { it.slide.hasImage() }
@ -263,7 +296,11 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
Box(modifier = Modifier.weight(1f)) { Box(modifier = Modifier.weight(1f)) {
CellPager(pagerState, imageAttachments, onClick) CellPager(pagerState, imageAttachments, onClick)
HorizontalPagerIndicator(pagerState) HorizontalPagerIndicator(pagerState)
ExpandButton(modifier = Modifier.align(Alignment.BottomEnd).padding(8.dp)) ExpandButton(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(8.dp)
)
} }
CarouselNextButton(pagerState) CarouselNextButton(pagerState)
} }
@ -276,7 +313,11 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
ExperimentalGlideComposeApi::class ExperimentalGlideComposeApi::class
) )
@Composable @Composable
private fun CellPager(pagerState: PagerState, imageAttachments: List<Attachment>, onClick: (Slide) -> Unit) { private fun CellPager(
pagerState: PagerState,
imageAttachments: List<Attachment>,
onClick: (Slide) -> Unit
) {
CellNoMargin { CellNoMargin {
HorizontalPager(state = pagerState) { i -> HorizontalPager(state = pagerState) { i ->
val slide = imageAttachments[i].slide val slide = imageAttachments[i].slide
@ -326,7 +367,8 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
TitledText( TitledText(
titledText, titledText,
modifier = modifier, modifier = modifier,
valueStyle = LocalTextStyle.current.copy(color = colorDestructive)) valueStyle = LocalTextStyle.current.copy(color = colorDestructive)
)
} }
@Composable @Composable
@ -334,7 +376,8 @@ class MessageDetailActivity : PassphraseRequiredActionBarActivity() {
TitledText( TitledText(
titledText, titledText,
modifier = modifier, modifier = modifier,
valueStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace)) valueStyle = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace)
)
} }
@Composable @Composable

View File

@ -3,6 +3,8 @@ package org.thoughtcrime.securesms.conversation.v2
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.utilities.Util import org.session.libsession.utilities.Util
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
@ -31,9 +33,10 @@ data class Attachment(
val fileDetails: List<TitledText> val fileDetails: List<TitledText>
) )
class MessageDetailsViewModel : ViewModel() { @HiltViewModel
@Inject class MessageDetailsViewModel @Inject constructor(
lateinit var attachmentDb: AttachmentDatabase private val attachmentDb: AttachmentDatabase
): ViewModel() {
fun setMessageRecord(value: MessageRecord?, error: String?) { fun setMessageRecord(value: MessageRecord?, error: String?) {
val mmsRecord = value as? MmsMessageRecord val mmsRecord = value as? MmsMessageRecord

View File

@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getInt
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.database.model.SmsMessageRecord import org.thoughtcrime.securesms.database.model.SmsMessageRecord
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.SearchUtil import org.thoughtcrime.securesms.util.SearchUtil
import org.thoughtcrime.securesms.util.getAccentColor import org.thoughtcrime.securesms.util.getAccentColor
@ -60,12 +61,12 @@ class VisibleMessageContentView : ConstraintLayout {
// region Updating // region Updating
fun bind( fun bind(
message: MessageRecord, message: MessageRecord,
isStartOfMessageCluster: Boolean, isStartOfMessageCluster: Boolean = true,
isEndOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean = true,
glide: GlideRequests, glide: GlideRequests = GlideApp.with(this),
thread: Recipient, thread: Recipient,
searchQuery: String?, searchQuery: String? = null,
contactIsTrusted: Boolean, contactIsTrusted: Boolean = true,
onAttachmentNeedsDownload: (Long, Long) -> Unit onAttachmentNeedsDownload: (Long, Long) -> Unit
) { ) {
// Background // Background

View File

@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.groups.OpenGroupManager import org.thoughtcrime.securesms.groups.OpenGroupManager
import org.thoughtcrime.securesms.home.UserDetailsBottomSheet import org.thoughtcrime.securesms.home.UserDetailsBottomSheet
import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.DateUtils
import org.thoughtcrime.securesms.util.disableClipping import org.thoughtcrime.securesms.util.disableClipping
@ -70,7 +71,6 @@ class VisibleMessageView : LinearLayout {
@Inject lateinit var mmsDb: MmsDatabase @Inject lateinit var mmsDb: MmsDatabase
private val binding by lazy { ViewVisibleMessageBinding.bind(this) } private val binding by lazy { ViewVisibleMessageBinding.bind(this) }
private val screenWidth = Resources.getSystem().displayMetrics.widthPixels
private val swipeToReplyIcon = ContextCompat.getDrawable(context, R.drawable.ic_baseline_reply_24)!!.mutate() private val swipeToReplyIcon = ContextCompat.getDrawable(context, R.drawable.ic_baseline_reply_24)!!.mutate()
private val swipeToReplyIconRect = Rect() private val swipeToReplyIconRect = Rect()
private var dx = 0.0f private var dx = 0.0f
@ -119,13 +119,13 @@ class VisibleMessageView : LinearLayout {
// region Updating // region Updating
fun bind( fun bind(
message: MessageRecord, message: MessageRecord,
previous: MessageRecord?, previous: MessageRecord? = null,
next: MessageRecord?, next: MessageRecord? = null,
glide: GlideRequests, glide: GlideRequests = GlideApp.with(this),
searchQuery: String?, searchQuery: String? = null,
contact: Contact?, contact: Contact? = null,
senderSessionID: String, senderSessionID: String,
delegate: VisibleMessageViewDelegate?, delegate: VisibleMessageViewDelegate? = null,
onAttachmentNeedsDownload: (Long, Long) -> Unit onAttachmentNeedsDownload: (Long, Long) -> Unit
) { ) {
val threadID = message.threadId val threadID = message.threadId

View File

@ -93,11 +93,13 @@ fun CellWithPaddingAndMargin(
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) { fun BoxScope.HorizontalPagerIndicator(pagerState: PagerState) {
Card(shape = RoundedCornerShape(50.dp), if (pagerState.pageCount >= 2) Card(
shape = RoundedCornerShape(50.dp),
backgroundColor = Color.Black.copy(alpha = 0.4f), backgroundColor = Color.Black.copy(alpha = 0.4f),
modifier = Modifier modifier = Modifier
.align(Alignment.BottomCenter) .align(Alignment.BottomCenter)
.padding(8.dp)) { .padding(8.dp)
) {
Box(modifier = Modifier.padding(8.dp)) { Box(modifier = Modifier.padding(8.dp)) {
HorizontalPagerIndicator( HorizontalPagerIndicator(
pagerState = pagerState, pagerState = pagerState,

View File

@ -1,3 +1,4 @@
buildscript { buildscript {
repositories { repositories {
google() google()
@ -12,6 +13,10 @@ buildscript {
} }
} }
plugins{
id("com.google.dagger.hilt.android") version "2.44" apply false
}
allprojects { allprojects {
repositories { repositories {
google() google()