refactor: auto attachment downloads for specific threads, manual attachment downloads triggered from UI and correct visual state representation

This commit is contained in:
0x330a 2022-11-11 12:52:00 +11:00
parent 5860c0b961
commit 995ac45ed9
8 changed files with 80 additions and 74 deletions

View file

@ -10,11 +10,12 @@ import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R
import network.loki.messenger.databinding.DialogDownloadBinding
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
import org.thoughtcrime.securesms.database.SessionContactDatabase
import org.thoughtcrime.securesms.util.createAndStartAttachmentDownload
import org.thoughtcrime.securesms.util.displaySize
import javax.inject.Inject
@ -47,7 +48,7 @@ class DownloadDialog(private val recipient: Recipient,
private fun download() {
// TODO: add attachment download job trigger with attachmentID and databaseMessageID
val downloadJob = AttachmentDownloadJob()
JobQueue.shared.createAndStartAttachmentDownload(databaseAttachment)
dismiss()
}
}

View file

@ -7,7 +7,7 @@ import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import network.loki.messenger.R
import network.loki.messenger.databinding.ViewPendingAttachmentBinding
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.dialogs.DownloadDialog
import org.thoughtcrime.securesms.util.ActivityDispatcher
@ -21,8 +21,6 @@ class PendingAttachmentView: LinearLayout {
MEDIA
}
private var attachmentId: AttachmentId? = null
// region Lifecycle
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
@ -31,7 +29,7 @@ class PendingAttachmentView: LinearLayout {
// endregion
// region Updating
fun bind(attachmentType: AttachmentType, @ColorInt textColor: Int, attachmentId: AttachmentId) {
fun bind(attachmentType: AttachmentType, @ColorInt textColor: Int, attachment: DatabaseAttachment) {
val (iconRes, stringRes) = when (attachmentType) {
AttachmentType.AUDIO -> R.drawable.ic_microphone to R.string.Slide_audio
AttachmentType.DOCUMENT -> R.drawable.ic_document_large_light to R.string.document
@ -43,15 +41,12 @@ class PendingAttachmentView: LinearLayout {
binding.untrustedAttachmentIcon.setImageDrawable(iconDrawable)
binding.untrustedAttachmentTitle.text = text
this.attachmentId = attachmentId
}
// endregion
// region Interaction
fun showDownloadDialog(threadRecipient: Recipient) {
attachmentId?.let { attachmentId ->
ActivityDispatcher.get(context)?.showDialog(DownloadDialog(threadRecipient, attachmentId))
}
fun showDownloadDialog(threadRecipient: Recipient, attachment: DatabaseAttachment) {
ActivityDispatcher.get(context)?.showDialog(DownloadDialog(threadRecipient, attachment))
}
}

View file

@ -26,9 +26,6 @@ import androidx.core.view.isVisible
import network.loki.messenger.R
import network.loki.messenger.databinding.ViewVisibleMessageContentBinding
import okhttp3.HttpUrl
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.utilities.getColorFromAttr
@ -65,7 +62,7 @@ class VisibleMessageContentView : LinearLayout {
// region Updating
fun bind(message: MessageRecord, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean,
glide: GlideRequests, thread: Recipient, searchQuery: String?, autoDownloadAttachments: Boolean) {
glide: GlideRequests, thread: Recipient, searchQuery: String?) {
// Background
val background = getBackground(message.isOutgoing)
val color = if (message.isOutgoing) context.getAccentColor()
@ -75,6 +72,7 @@ class VisibleMessageContentView : LinearLayout {
binding.contentParent.background = background
val mediaDownloaded = message is MmsMessageRecord && message.slideDeck.asAttachments().all { it.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_DONE }
val mediaInProgress = message is MmsMessageRecord && message.slideDeck.asAttachments().any { it.isInProgress }
val mediaThumbnailMessage = message is MmsMessageRecord && message.slideDeck.thumbnailSlide != null
// reset visibilities / containers
@ -89,7 +87,7 @@ class VisibleMessageContentView : LinearLayout {
} else {
binding.deletedMessageView.root.isVisible = false
}
// clear the
// clear the body
binding.bodyTextView.text = null
@ -97,9 +95,9 @@ class VisibleMessageContentView : LinearLayout {
binding.linkPreviewView.isVisible = message is MmsMessageRecord && message.linkPreviews.isNotEmpty()
binding.pendingAttachmentView.root.isVisible = !mediaDownloaded && message is MmsMessageRecord && message.quote == null && message.linkPreviews.isEmpty()
binding.voiceMessageView.root.isVisible = mediaDownloaded && message is MmsMessageRecord && message.slideDeck.audioSlide != null
binding.documentView.root.isVisible = mediaDownloaded && message is MmsMessageRecord && message.slideDeck.documentSlide != null
binding.pendingAttachmentView.root.isVisible = !mediaDownloaded && !mediaInProgress && message is MmsMessageRecord && message.quote == null && message.linkPreviews.isEmpty()
binding.voiceMessageView.root.isVisible = (mediaDownloaded || mediaInProgress) && message is MmsMessageRecord && message.slideDeck.audioSlide != null
binding.documentView.root.isVisible = (mediaDownloaded || mediaInProgress) && message is MmsMessageRecord && message.slideDeck.documentSlide != null
binding.albumThumbnailView.isVisible = mediaThumbnailMessage
binding.openGroupInvitationView.root.isVisible = message.isOpenGroupInvitation
@ -125,36 +123,18 @@ class VisibleMessageContentView : LinearLayout {
}
}
if (message is MmsMessageRecord) {
message.slideDeck.asAttachments().forEach { attach ->
val dbAttachment = attach as? DatabaseAttachment ?: return@forEach
val attachmentId = dbAttachment.attachmentId.rowId
if (attach.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING
&& MessagingModuleConfiguration.shared.storage.getAttachmentUploadJob(attachmentId) == null) {
// start download
JobQueue.shared.add(AttachmentDownloadJob(attachmentId, dbAttachment.mmsId))
}
}
message.linkPreviews.forEach { preview ->
val previewThumbnail = preview.getThumbnail().orNull() as? DatabaseAttachment ?: return@forEach
val attachmentId = previewThumbnail.attachmentId.rowId
if (previewThumbnail.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING
&& MessagingModuleConfiguration.shared.storage.getAttachmentUploadJob(attachmentId) == null) {
JobQueue.shared.add(AttachmentDownloadJob(attachmentId, previewThumbnail.mmsId))
}
}
}
when {
// LINK PREVIEW
message is MmsMessageRecord && message.linkPreviews.isNotEmpty() -> {
binding.linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster)
onContentClick.add { event -> binding.linkPreviewView.calculateHit(event) }
// Body text view is inside the link preview for layout convenience
}
// AUDIO
message is MmsMessageRecord && message.slideDeck.audioSlide != null -> {
hideBody = true
// Audio attachment
if (mediaDownloaded || message.isOutgoing) {
if (mediaDownloaded || mediaInProgress || message.isOutgoing) {
binding.voiceMessageView.root.indexInAdapter = indexInAdapter
binding.voiceMessageView.root.delegate = context as? ConversationActivityV2
binding.voiceMessageView.root.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
@ -163,33 +143,36 @@ class VisibleMessageContentView : LinearLayout {
onContentClick.add { binding.voiceMessageView.root.togglePlayback() }
onContentDoubleTap = { binding.voiceMessageView.root.handleDoubleTap() }
} else {
binding.pendingAttachmentView.root.bind(
PendingAttachmentView.AttachmentType.AUDIO,
getTextColor(context,message),
)
onContentClick.add { binding.pendingAttachmentView.root.showDownloadDialog(message.recipient) }
(message.slideDeck.audioSlide?.asAttachment() as? DatabaseAttachment)?.let { attachment ->
binding.pendingAttachmentView.root.bind(
PendingAttachmentView.AttachmentType.AUDIO,
getTextColor(context,message),
attachment
)
onContentClick.add { binding.pendingAttachmentView.root.showDownloadDialog(message.recipient, attachment) }
}
}
}
// DOCUMENT
message is MmsMessageRecord && message.slideDeck.documentSlide != null -> {
hideBody = true
// Document attachment
if (mediaDownloaded || message.isOutgoing) {
if (mediaDownloaded || mediaInProgress || message.isOutgoing) {
binding.documentView.root.bind(message, getTextColor(context, message))
} else {
binding.pendingAttachmentView.root.bind(
PendingAttachmentView.AttachmentType.DOCUMENT,
getTextColor(context,message),
)
onContentClick.add { binding.pendingAttachmentView.root.showDownloadDialog(message.recipient) }
(message.slideDeck.documentSlide?.asAttachment() as? DatabaseAttachment)?.let { attachment ->
binding.pendingAttachmentView.root.bind(
PendingAttachmentView.AttachmentType.DOCUMENT,
getTextColor(context,message),
attachment
)
onContentClick.add { binding.pendingAttachmentView.root.showDownloadDialog(message.recipient, attachment) }
}
}
}
// IMAGE / VIDEO
message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty() -> {
/*
* Images / Video attachment
*/
if (mediaDownloaded || message.isOutgoing) {
if (mediaDownloaded || mediaInProgress || message.isOutgoing) {
// isStart and isEnd of cluster needed for calculating the mask for full bubble image groups
// bind after add view because views are inflated and calculated during bind
binding.albumThumbnailView.bind(
@ -207,12 +190,17 @@ class VisibleMessageContentView : LinearLayout {
} else {
hideBody = true
binding.albumThumbnailView.clearViews()
binding.pendingAttachmentView.root.bind(
PendingAttachmentView.AttachmentType.MEDIA,
getTextColor(context,message),
)
onContentClick.add { binding.pendingAttachmentView.root.showDownloadDialog(message.recipient) }
val firstAttachment = message.slideDeck.asAttachments().first() as? DatabaseAttachment
firstAttachment?.let { attachment ->
binding.pendingAttachmentView.root.bind(
PendingAttachmentView.AttachmentType.MEDIA,
getTextColor(context,message),
attachment
)
onContentClick.add {
binding.pendingAttachmentView.root.showDownloadDialog(message.recipient, attachment)
}
}
}
}
message.isOpenGroupInvitation -> {

View file

@ -229,8 +229,7 @@ class VisibleMessageView : LinearLayout {
isEndOfMessageCluster,
glide,
thread,
searchQuery,
message.isOutgoing || (thread.autoDownloadAttachments)
searchQuery
)
binding.messageContentView.delegate = delegate
onDoubleTap = { binding.messageContentView.onContentDoubleTap?.invoke() }

View file

@ -1,18 +1,35 @@
package org.thoughtcrime.securesms.util
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.sending_receiving.attachments.Attachment
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentTransferProgress
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
private const val ZERO_SIZE = "0.00"
private const val KILO_SIZE = 1024
private const val KILO_SIZE = 1024f
private const val MB_SUFFIX = "MB"
private const val KB_SUFFIX = "KB"
fun Attachment.displaySize(): String {
val kbSize = size / KILO_SIZE
val needsMb = kbSize > KILO_SIZE
val sizeText = "%.2f".format(if (needsMb) kbSize / KILO_SIZE else kbSize)
return when {
val displaySize = when {
sizeText == ZERO_SIZE -> "0.01"
sizeText.endsWith(".00") -> sizeText.takeWhile { it != '.' }
else -> sizeText
}
return "$displaySize${if (needsMb) MB_SUFFIX else KB_SUFFIX}"
}
fun JobQueue.createAndStartAttachmentDownload(attachment: DatabaseAttachment) {
val attachmentId = attachment.attachmentId.rowId
if (attachment.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING
&& MessagingModuleConfiguration.shared.storage.getAttachmentUploadJob(attachmentId) == null) {
// start download
add(AttachmentDownloadJob(attachmentId, attachment.mmsId))
}
}

View file

@ -99,12 +99,6 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
handleFailure(Error.NoSender, null)
return
}
if (!threadRecipient.isGroupRecipient && (!threadRecipient.autoDownloadAttachments && storage.getUserPublicKey() != sender)) {
// if we aren't receiving a group message, a message from ourselves (self-send) and the thread does not auto-download:
// do not continue, but do not fail (it will be restarted later)
// TODO: probably remove this logic and just re-trigger based on attachment ID in future
return
}
var tempFile: File? = null
try {

View file

@ -3,6 +3,7 @@ package org.session.libsession.messaging.sending_receiving
import android.text.TextUtils
import org.session.libsession.avatars.AvatarHelper
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.jobs.AttachmentDownloadJob
import org.session.libsession.messaging.jobs.BackgroundGroupAddJob
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.messages.Message
@ -325,6 +326,16 @@ fun MessageReceiver.handleVisibleMessage(message: VisibleMessage,
storage.persist(message, quoteModel, linkPreviews, message.groupPublicKey, openGroupID,
attachments, runIncrement, runThreadUpdate
) ?: return null
// Parse & persist attachments
// Start attachment downloads if needed
if (threadRecipient?.autoDownloadAttachments == true || messageSender == userPublicKey) {
storage.getAttachmentsForMessage(messageID).iterator().forEach { attachment ->
attachment.attachmentId?.let { id ->
val downloadJob = AttachmentDownloadJob(id.rowId, messageID)
JobQueue.shared.add(downloadJob)
}
}
}
val openGroupServerID = message.openGroupServerMessageID
if (openGroupServerID != null) {
val isSms = !(message.isMediaMessage() || attachments.isNotEmpty())

View file

@ -76,7 +76,8 @@ public abstract class Attachment {
public boolean isInProgress() {
return transferState != AttachmentTransferProgress.TRANSFER_PROGRESS_DONE &&
transferState != AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED;
transferState != AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED &&
transferState != AttachmentTransferProgress.TRANSFER_PROGRESS_PENDING;
}
public long getSize() {