mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-21 15:48:26 +00:00
Merge pull request #647 from hjubb/trusted_attachment_download
Only Download Attachments from Trusted Contacts
This commit is contained in:
commit
462a7ed977
@ -9,6 +9,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.*
|
|||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.UploadResult
|
import org.session.libsession.utilities.UploadResult
|
||||||
import org.session.libsession.utilities.Util
|
import org.session.libsession.utilities.Util
|
||||||
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.messages.SignalServiceAttachment
|
import org.session.libsignal.messages.SignalServiceAttachment
|
||||||
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
||||||
import org.session.libsignal.messages.SignalServiceAttachmentStream
|
import org.session.libsignal.messages.SignalServiceAttachmentStream
|
||||||
@ -60,9 +61,9 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
|||||||
return databaseAttachment.toSignalAttachmentPointer()
|
return databaseAttachment.toSignalAttachmentPointer()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setAttachmentState(attachmentState: AttachmentState, attachmentId: Long, messageID: Long) {
|
override fun setAttachmentState(attachmentState: AttachmentState, attachmentId: AttachmentId, messageID: Long) {
|
||||||
val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context)
|
val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context)
|
||||||
attachmentDatabase.setTransferState(messageID, AttachmentId(attachmentId, 0), attachmentState.value)
|
attachmentDatabase.setTransferState(messageID, attachmentId, attachmentState.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMessageForQuote(timestamp: Long, author: Address): Pair<Long, Boolean>? {
|
override fun getMessageForQuote(timestamp: Long, author: Address): Pair<Long, Boolean>? {
|
||||||
@ -92,6 +93,14 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
|||||||
return message.linkPreviews.firstOrNull()?.attachmentId?.rowId
|
return message.linkPreviews.firstOrNull()?.attachmentId?.rowId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getIndividualRecipientForMms(mmsId: Long): Recipient? {
|
||||||
|
val mmsDb = DatabaseFactory.getMmsDatabase(context)
|
||||||
|
val message = mmsDb.getMessage(mmsId).use {
|
||||||
|
mmsDb.readerFor(it).next
|
||||||
|
}
|
||||||
|
return message?.individualRecipient
|
||||||
|
}
|
||||||
|
|
||||||
override fun insertAttachment(messageId: Long, attachmentId: AttachmentId, stream: InputStream) {
|
override fun insertAttachment(messageId: Long, attachmentId: AttachmentId, stream: InputStream) {
|
||||||
val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context)
|
val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context)
|
||||||
attachmentDatabase.insertAttachmentsForPlaceholder(messageId, attachmentId, stream)
|
attachmentDatabase.insertAttachmentsForPlaceholder(messageId, attachmentId, stream)
|
||||||
@ -110,6 +119,13 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
|
|||||||
), threadId)
|
), threadId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isMmsOutgoing(mmsMessageId: Long): Boolean {
|
||||||
|
val mmsDb = DatabaseFactory.getMmsDatabase(context)
|
||||||
|
return mmsDb.getMessage(mmsMessageId).use { cursor ->
|
||||||
|
mmsDb.readerFor(cursor).next
|
||||||
|
}.isOutgoing
|
||||||
|
}
|
||||||
|
|
||||||
override fun isOutgoingMessage(timestamp: Long): Boolean {
|
override fun isOutgoingMessage(timestamp: Long): Boolean {
|
||||||
val smsDatabase = DatabaseFactory.getSmsDatabase(context)
|
val smsDatabase = DatabaseFactory.getSmsDatabase(context)
|
||||||
val mmsDatabase = DatabaseFactory.getMmsDatabase(context)
|
val mmsDatabase = DatabaseFactory.getMmsDatabase(context)
|
||||||
|
@ -90,6 +90,7 @@ import org.thoughtcrime.securesms.conversation.v2.messages.*
|
|||||||
import org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar
|
import org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar
|
||||||
import org.thoughtcrime.securesms.conversation.v2.search.SearchViewModel
|
import org.thoughtcrime.securesms.conversation.v2.search.SearchViewModel
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager
|
import org.thoughtcrime.securesms.conversation.v2.utilities.AttachmentManager
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
import org.thoughtcrime.securesms.database.DraftDatabase
|
import org.thoughtcrime.securesms.database.DraftDatabase
|
||||||
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts
|
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts
|
||||||
@ -264,6 +265,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
push(intent, false)
|
push(intent, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun showDialog(baseDialog: BaseDialog, tag: String?) {
|
||||||
|
baseDialog.show(supportFragmentManager, tag)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setUpRecyclerView() {
|
private fun setUpRecyclerView() {
|
||||||
conversationRecyclerView.adapter = adapter
|
conversationRecyclerView.adapter = adapter
|
||||||
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)
|
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)
|
||||||
|
@ -13,6 +13,10 @@ import androidx.core.view.children
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import kotlinx.android.synthetic.main.album_thumbnail_view.view.*
|
import kotlinx.android.synthetic.main.album_thumbnail_view.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
|
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.ViewUtil
|
import org.session.libsession.utilities.ViewUtil
|
||||||
import org.session.libsession.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.MediaPreviewActivity
|
import org.thoughtcrime.securesms.MediaPreviewActivity
|
||||||
@ -23,6 +27,7 @@ import org.thoughtcrime.securesms.util.ActivityDispatcher
|
|||||||
import org.thoughtcrime.securesms.longmessage.LongMessageActivity
|
import org.thoughtcrime.securesms.longmessage.LongMessageActivity
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.mms.Slide
|
import org.thoughtcrime.securesms.mms.Slide
|
||||||
|
import org.thoughtcrime.securesms.video.exo.AttachmentDataSource
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class AlbumThumbnailView : FrameLayout {
|
class AlbumThumbnailView : FrameLayout {
|
||||||
@ -82,6 +87,13 @@ class AlbumThumbnailView : FrameLayout {
|
|||||||
// hit intersects with this particular child
|
// hit intersects with this particular child
|
||||||
val slide = slides.getOrNull(index) ?: return
|
val slide = slides.getOrNull(index) ?: return
|
||||||
// only open to downloaded images
|
// only open to downloaded images
|
||||||
|
if (slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED) {
|
||||||
|
// restart download here
|
||||||
|
(slide.asAttachment() as? DatabaseAttachment)?.let { attachment ->
|
||||||
|
val attachmentId = attachment.attachmentId.rowId
|
||||||
|
JobQueue.shared.add(AttachmentDownloadJob(attachmentId, mms.getId()))
|
||||||
|
}
|
||||||
|
}
|
||||||
if (slide.isInProgress) return
|
if (slide.isInProgress) return
|
||||||
|
|
||||||
ActivityDispatcher.get(context)?.dispatchIntent { context ->
|
ActivityDispatcher.get(context)?.dispatchIntent { context ->
|
||||||
|
@ -9,6 +9,8 @@ import androidx.appcompat.app.AlertDialog
|
|||||||
import kotlinx.android.synthetic.main.dialog_download.view.*
|
import kotlinx.android.synthetic.main.dialog_download.view.*
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.messaging.contacts.Contact
|
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.utilities.recipients.Recipient
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||||
@ -36,6 +38,12 @@ class DownloadDialog(private val recipient: Recipient) : BaseDialog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun trust() {
|
private fun trust() {
|
||||||
// TODO: Implement
|
val contactDB = DatabaseFactory.getSessionContactDatabase(requireContext())
|
||||||
|
val sessionID = recipient.address.toString()
|
||||||
|
val contact = contactDB.getContactWithSessionID(sessionID) ?: return
|
||||||
|
val threadID = DatabaseFactory.getThreadDatabase(requireContext()).getThreadIdIfExistsFor(recipient)
|
||||||
|
contactDB.setContactIsTrusted(contact, true, threadID)
|
||||||
|
JobQueue.shared.resumePendingJobs(AttachmentDownloadJob.KEY)
|
||||||
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package org.thoughtcrime.securesms.conversation.v2.messages
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import kotlinx.android.synthetic.main.view_untrusted_attachment.view.*
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.dialogs.DownloadDialog
|
||||||
|
import org.thoughtcrime.securesms.util.ActivityDispatcher
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class UntrustedAttachmentView: LinearLayout {
|
||||||
|
|
||||||
|
enum class AttachmentType {
|
||||||
|
AUDIO,
|
||||||
|
DOCUMENT,
|
||||||
|
MEDIA
|
||||||
|
}
|
||||||
|
|
||||||
|
// region Lifecycle
|
||||||
|
constructor(context: Context) : super(context) { initialize() }
|
||||||
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
|
||||||
|
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
|
||||||
|
|
||||||
|
private fun initialize() {
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.view_untrusted_attachment, this)
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Updating
|
||||||
|
fun bind(attachmentType: AttachmentType, @ColorInt textColor: Int) {
|
||||||
|
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
|
||||||
|
AttachmentType.MEDIA -> R.drawable.ic_image_white_24dp to R.string.media
|
||||||
|
}
|
||||||
|
val iconDrawable = ContextCompat.getDrawable(context,iconRes)!!
|
||||||
|
iconDrawable.mutate().setTint(textColor)
|
||||||
|
val text = context.getString(R.string.UntrustedAttachmentView_download_attachment, context.getString(stringRes).toLowerCase(Locale.ROOT))
|
||||||
|
|
||||||
|
untrustedAttachmentIcon.setImageDrawable(iconDrawable)
|
||||||
|
untrustedAttachmentTitle.text = text
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Interaction
|
||||||
|
fun showTrustDialog(recipient: Recipient) {
|
||||||
|
ActivityDispatcher.get(context)?.showDialog(DownloadDialog(recipient))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -63,7 +63,7 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
|
|
||||||
// region Updating
|
// region Updating
|
||||||
fun bind(message: MessageRecord, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean,
|
fun bind(message: MessageRecord, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean,
|
||||||
glide: GlideRequests, maxWidth: Int, thread: Recipient, searchQuery: String?) {
|
glide: GlideRequests, maxWidth: Int, thread: Recipient, searchQuery: String?, contactIsTrusted: Boolean) {
|
||||||
// Background
|
// Background
|
||||||
val background = getBackground(message.isOutgoing, isStartOfMessageCluster, isEndOfMessageCluster)
|
val background = getBackground(message.isOutgoing, isStartOfMessageCluster, isEndOfMessageCluster)
|
||||||
val colorID = if (message.isOutgoing) R.attr.message_sent_background_color else R.attr.message_received_background_color
|
val colorID = if (message.isOutgoing) R.attr.message_sent_background_color else R.attr.message_received_background_color
|
||||||
@ -108,32 +108,56 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
|
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
|
||||||
val voiceMessageView = VoiceMessageView(context)
|
// Audio attachment
|
||||||
voiceMessageView.index = viewHolderIndex
|
if (contactIsTrusted || message.isOutgoing) {
|
||||||
voiceMessageView.delegate = context as? ConversationActivityV2
|
val voiceMessageView = VoiceMessageView(context)
|
||||||
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
voiceMessageView.index = viewHolderIndex
|
||||||
mainContainer.addView(voiceMessageView)
|
voiceMessageView.delegate = context as? ConversationActivityV2
|
||||||
// We have to use onContentClick (rather than a click listener directly on the voice
|
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
|
||||||
// message view) so as to not interfere with all the other gestures.
|
mainContainer.addView(voiceMessageView)
|
||||||
onContentClick = { voiceMessageView.togglePlayback() }
|
// We have to use onContentClick (rather than a click listener directly on the voice
|
||||||
onContentDoubleTap = { voiceMessageView.handleDoubleTap() }
|
// message view) so as to not interfere with all the other gestures.
|
||||||
|
onContentClick = { voiceMessageView.togglePlayback() }
|
||||||
|
onContentDoubleTap = { voiceMessageView.handleDoubleTap() }
|
||||||
|
} else {
|
||||||
|
val untrustedView = UntrustedAttachmentView(context)
|
||||||
|
untrustedView.bind(UntrustedAttachmentView.AttachmentType.AUDIO, VisibleMessageContentView.getTextColor(context,message))
|
||||||
|
mainContainer.addView(untrustedView)
|
||||||
|
onContentClick = { untrustedView.showTrustDialog(message.individualRecipient) }
|
||||||
|
}
|
||||||
} else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) {
|
} else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) {
|
||||||
val documentView = DocumentView(context)
|
// Document attachment
|
||||||
documentView.bind(message, VisibleMessageContentView.getTextColor(context, message))
|
if (contactIsTrusted || message.isOutgoing) {
|
||||||
mainContainer.addView(documentView)
|
val documentView = DocumentView(context)
|
||||||
|
documentView.bind(message, VisibleMessageContentView.getTextColor(context, message))
|
||||||
|
mainContainer.addView(documentView)
|
||||||
|
} else {
|
||||||
|
val untrustedView = UntrustedAttachmentView(context)
|
||||||
|
untrustedView.bind(UntrustedAttachmentView.AttachmentType.DOCUMENT, VisibleMessageContentView.getTextColor(context,message))
|
||||||
|
mainContainer.addView(untrustedView)
|
||||||
|
onContentClick = { untrustedView.showTrustDialog(message.individualRecipient) }
|
||||||
|
}
|
||||||
} else if (message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty()) {
|
} else if (message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty()) {
|
||||||
val albumThumbnailView = AlbumThumbnailView(context)
|
// Images/Video attachment
|
||||||
mainContainer.addView(albumThumbnailView)
|
if (contactIsTrusted || message.isOutgoing) {
|
||||||
// isStart and isEnd of cluster needed for calculating the mask for full bubble image groups
|
val albumThumbnailView = AlbumThumbnailView(context)
|
||||||
// bind after add view because views are inflated and calculated during bind
|
mainContainer.addView(albumThumbnailView)
|
||||||
albumThumbnailView.bind(
|
// isStart and isEnd of cluster needed for calculating the mask for full bubble image groups
|
||||||
glideRequests = glide,
|
// bind after add view because views are inflated and calculated during bind
|
||||||
message = message,
|
albumThumbnailView.bind(
|
||||||
isStart = isStartOfMessageCluster,
|
glideRequests = glide,
|
||||||
isEnd = isEndOfMessageCluster
|
message = message,
|
||||||
)
|
isStart = isStartOfMessageCluster,
|
||||||
onContentClick = { event ->
|
isEnd = isEndOfMessageCluster
|
||||||
albumThumbnailView.calculateHitObject(event, message, thread)
|
)
|
||||||
|
onContentClick = { event ->
|
||||||
|
albumThumbnailView.calculateHitObject(event, message, thread)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val untrustedView = UntrustedAttachmentView(context)
|
||||||
|
untrustedView.bind(UntrustedAttachmentView.AttachmentType.MEDIA, VisibleMessageContentView.getTextColor(context,message))
|
||||||
|
mainContainer.addView(untrustedView)
|
||||||
|
onContentClick = { untrustedView.showTrustDialog(message.individualRecipient) }
|
||||||
}
|
}
|
||||||
} else if (message.isOpenGroupInvitation) {
|
} else if (message.isOpenGroupInvitation) {
|
||||||
val openGroupInvitationView = OpenGroupInvitationView(context)
|
val openGroupInvitationView = OpenGroupInvitationView(context)
|
||||||
|
@ -81,6 +81,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
val threadDB = DatabaseFactory.getThreadDatabase(context)
|
val threadDB = DatabaseFactory.getThreadDatabase(context)
|
||||||
val thread = threadDB.getRecipientForThreadId(threadID) ?: return
|
val thread = threadDB.getRecipientForThreadId(threadID) ?: return
|
||||||
val contactDB = DatabaseFactory.getSessionContactDatabase(context)
|
val contactDB = DatabaseFactory.getSessionContactDatabase(context)
|
||||||
|
val contact = contactDB.getContactWithSessionID(senderSessionID)
|
||||||
val isGroupThread = thread.isGroupRecipient
|
val isGroupThread = thread.isGroupRecipient
|
||||||
val isStartOfMessageCluster = isStartOfMessageCluster(message, previous, isGroupThread)
|
val isStartOfMessageCluster = isStartOfMessageCluster(message, previous, isGroupThread)
|
||||||
val isEndOfMessageCluster = isEndOfMessageCluster(message, next, isGroupThread)
|
val isEndOfMessageCluster = isEndOfMessageCluster(message, next, isGroupThread)
|
||||||
@ -100,7 +101,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
}
|
}
|
||||||
senderNameTextView.isVisible = isStartOfMessageCluster
|
senderNameTextView.isVisible = isStartOfMessageCluster
|
||||||
val context = if (thread.isOpenGroupRecipient) ContactContext.OPEN_GROUP else ContactContext.REGULAR
|
val context = if (thread.isOpenGroupRecipient) ContactContext.OPEN_GROUP else ContactContext.REGULAR
|
||||||
senderNameTextView.text = contactDB.getContactWithSessionID(senderSessionID)?.displayName(context) ?: senderSessionID
|
senderNameTextView.text = contact?.displayName(context) ?: senderSessionID
|
||||||
} else {
|
} else {
|
||||||
profilePictureContainer.visibility = View.GONE
|
profilePictureContainer.visibility = View.GONE
|
||||||
senderNameTextView.visibility = View.GONE
|
senderNameTextView.visibility = View.GONE
|
||||||
@ -149,7 +150,7 @@ class VisibleMessageView : LinearLayout {
|
|||||||
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
|
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
|
||||||
// Populate content view
|
// Populate content view
|
||||||
messageContentView.viewHolderIndex = viewHolderIndex
|
messageContentView.viewHolderIndex = viewHolderIndex
|
||||||
messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread, searchQuery)
|
messageContentView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster, glide, maxWidth, thread, searchQuery, isGroupThread || (contact?.isTrusted ?: false))
|
||||||
messageContentView.delegate = contentViewDelegate
|
messageContentView.delegate = contentViewDelegate
|
||||||
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
|
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
|
|||||||
private var progress = 0.0
|
private var progress = 0.0
|
||||||
private var duration = 0L
|
private var duration = 0L
|
||||||
private var player: AudioSlidePlayer? = null
|
private var player: AudioSlidePlayer? = null
|
||||||
private var isPreparing = false
|
|
||||||
var delegate: VoiceMessageViewDelegate? = null
|
var delegate: VoiceMessageViewDelegate? = null
|
||||||
var index = -1
|
var index = -1
|
||||||
|
|
||||||
@ -51,7 +50,7 @@ class VoiceMessageView : LinearLayout, AudioSlidePlayer.Listener {
|
|||||||
// region Updating
|
// region Updating
|
||||||
fun bind(message: MmsMessageRecord, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean) {
|
fun bind(message: MmsMessageRecord, isStartOfMessageCluster: Boolean, isEndOfMessageCluster: Boolean) {
|
||||||
val audio = message.slideDeck.audioSlide!!
|
val audio = message.slideDeck.audioSlide!!
|
||||||
voiceMessageViewLoader.isVisible = audio.isPendingDownload
|
voiceMessageViewLoader.isVisible = audio.isInProgress
|
||||||
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
|
val cornerRadii = MessageBubbleUtilities.calculateRadii(context, isStartOfMessageCluster, isEndOfMessageCluster, message.isOutgoing)
|
||||||
cornerMask.setTopLeftRadius(cornerRadii[0])
|
cornerMask.setTopLeftRadius(cornerRadii[0])
|
||||||
cornerMask.setTopRightRadius(cornerRadii[1])
|
cornerMask.setTopRightRadius(cornerRadii[1])
|
||||||
|
@ -40,6 +40,7 @@ open class KThumbnailView: FrameLayout {
|
|||||||
private val image by lazy { thumbnail_image }
|
private val image by lazy { thumbnail_image }
|
||||||
private val playOverlay by lazy { play_overlay }
|
private val playOverlay by lazy { play_overlay }
|
||||||
val loadIndicator: View by lazy { thumbnail_load_indicator }
|
val loadIndicator: View by lazy { thumbnail_load_indicator }
|
||||||
|
val downloadIndicator: View by lazy { thumbnail_download_icon }
|
||||||
|
|
||||||
private val dimensDelegate = ThumbnailDimensDelegate()
|
private val dimensDelegate = ThumbnailDimensDelegate()
|
||||||
|
|
||||||
@ -108,7 +109,8 @@ open class KThumbnailView: FrameLayout {
|
|||||||
|
|
||||||
this.slide = slide
|
this.slide = slide
|
||||||
|
|
||||||
loadIndicator.isVisible = slide.isInProgress && !mms.isFailed
|
loadIndicator.isVisible = slide.isInProgress
|
||||||
|
downloadIndicator.isVisible = slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED
|
||||||
|
|
||||||
dimensDelegate.setDimens(naturalWidth, naturalHeight)
|
dimensDelegate.setDimens(naturalWidth, naturalHeight)
|
||||||
invalidate()
|
invalidate()
|
||||||
|
@ -45,6 +45,17 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
|
|||||||
}.toSet()
|
}.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setContactIsTrusted(contact: Contact, isTrusted: Boolean, threadID: Long) {
|
||||||
|
val database = databaseHelper.writableDatabase
|
||||||
|
val contentValues = ContentValues(1)
|
||||||
|
contentValues.put(Companion.isTrusted, if (isTrusted) 1 else 0)
|
||||||
|
database.update(sessionContactTable, contentValues, "$sessionID = ?", arrayOf( contact.sessionID ))
|
||||||
|
if (threadID >= 0) {
|
||||||
|
notifyConversationListeners(threadID)
|
||||||
|
}
|
||||||
|
notifyConversationListListeners()
|
||||||
|
}
|
||||||
|
|
||||||
fun setContact(contact: Contact) {
|
fun setContact(contact: Contact) {
|
||||||
val database = databaseHelper.writableDatabase
|
val database = databaseHelper.writableDatabase
|
||||||
val contentValues = ContentValues(8)
|
val contentValues = ContentValues(8)
|
||||||
@ -56,7 +67,7 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
|
|||||||
contact.profilePictureEncryptionKey?.let {
|
contact.profilePictureEncryptionKey?.let {
|
||||||
contentValues.put(profilePictureEncryptionKey, Base64.encodeBytes(it))
|
contentValues.put(profilePictureEncryptionKey, Base64.encodeBytes(it))
|
||||||
}
|
}
|
||||||
contentValues.put(threadID, threadID)
|
contentValues.put(threadID, contact.threadID)
|
||||||
contentValues.put(isTrusted, if (contact.isTrusted) 1 else 0)
|
contentValues.put(isTrusted, if (contact.isTrusted) 1 else 0)
|
||||||
database.insertOrUpdate(sessionContactTable, contentValues, "$sessionID = ?", arrayOf( contact.sessionID ))
|
database.insertOrUpdate(sessionContactTable, contentValues, "$sessionID = ?", arrayOf( contact.sessionID ))
|
||||||
notifyConversationListListeners()
|
notifyConversationListListeners()
|
||||||
|
@ -503,9 +503,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
val mmsDb = DatabaseFactory.getMmsDatabase(context)
|
val mmsDb = DatabaseFactory.getMmsDatabase(context)
|
||||||
val cursor = mmsDb.getMessage(mmsId)
|
val cursor = mmsDb.getMessage(mmsId)
|
||||||
val reader = mmsDb.readerFor(cursor)
|
val reader = mmsDb.readerFor(cursor)
|
||||||
val threadId = reader.next.threadId
|
val threadId = reader.next?.threadId
|
||||||
cursor.close()
|
cursor.close()
|
||||||
return threadId
|
return threadId ?: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getContactWithSessionID(sessionID: String): Contact? {
|
override fun getContactWithSessionID(sessionID: String): Contact? {
|
||||||
@ -520,6 +520,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
|||||||
DatabaseFactory.getSessionContactDatabase(context).setContact(contact)
|
DatabaseFactory.getSessionContactDatabase(context).setContact(contact)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getRecipientForThread(threadId: Long): Recipient? {
|
||||||
|
return DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getRecipientSettings(address: Address): Recipient.RecipientSettings? {
|
override fun getRecipientSettings(address: Address): Recipient.RecipientSettings? {
|
||||||
val recipientSettings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(address)
|
val recipientSettings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(address)
|
||||||
return if (recipientSettings.isPresent) { recipientSettings.get() } else null
|
return if (recipientSettings.isPresent) { recipientSettings.get() } else null
|
||||||
|
@ -10,6 +10,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.thoughtcrime.securesms.BaseActionBarActivity
|
import org.thoughtcrime.securesms.BaseActionBarActivity
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
||||||
|
|
||||||
fun BaseActionBarActivity.setUpActionBarSessionLogo(hideBackButton: Boolean = false) {
|
fun BaseActionBarActivity.setUpActionBarSessionLogo(hideBackButton: Boolean = false) {
|
||||||
val actionbar = supportActionBar!!
|
val actionbar = supportActionBar!!
|
||||||
@ -65,4 +66,5 @@ interface ActivityDispatcher {
|
|||||||
fun get(context: Context) = context.getSystemService(SERVICE) as? ActivityDispatcher
|
fun get(context: Context) = context.getSystemService(SERVICE) as? ActivityDispatcher
|
||||||
}
|
}
|
||||||
fun dispatchIntent(body: (Context)->Intent?)
|
fun dispatchIntent(body: (Context)->Intent?)
|
||||||
|
fun showDialog(baseDialog: BaseDialog, tag: String? = null)
|
||||||
}
|
}
|
@ -22,6 +22,15 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:src="@drawable/ic_download_circle_filled_48"
|
||||||
|
android:id="@+id/thumbnail_download_icon"
|
||||||
|
android:layout_width="@dimen/medium_button_height"
|
||||||
|
android:layout_height="@dimen/medium_button_height"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:visibility="gone"
|
||||||
|
/>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/play_overlay"
|
android:id="@+id/play_overlay"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
@ -29,8 +38,7 @@
|
|||||||
android:background="@drawable/circle_white"
|
android:background="@drawable/circle_white"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:longClickable="false"
|
android:longClickable="false"
|
||||||
android:visibility="gone"
|
android:visibility="gone">
|
||||||
tools:visibility="visible">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="19dp"
|
android:layout_width="19dp"
|
||||||
|
30
app/src/main/res/layout/view_untrusted_attachment.xml
Normal file
30
app/src/main/res/layout/view_untrusted_attachment.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="@dimen/medium_spacing"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/untrustedAttachmentIcon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@drawable/ic_document_large_light"
|
||||||
|
app:tint="@color/text" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/untrustedAttachmentTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:textSize="@dimen/small_font_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
tools:text="I'm a very long document title. Did you know that?"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -874,4 +874,6 @@
|
|||||||
<string name="activity_conversation_blocked_banner_text">%s is blocked. Unblock them?</string>
|
<string name="activity_conversation_blocked_banner_text">%s is blocked. Unblock them?</string>
|
||||||
|
|
||||||
<string name="activity_conversation_attachment_prep_failed">Failed to prepare attachment for sending.</string>
|
<string name="activity_conversation_attachment_prep_failed">Failed to prepare attachment for sending.</string>
|
||||||
|
<string name="media">Media</string>
|
||||||
|
<string name="UntrustedAttachmentView_download_attachment">Tap to download %s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -3,6 +3,7 @@ package org.session.libsession.database
|
|||||||
import org.session.libsession.messaging.sending_receiving.attachments.*
|
import org.session.libsession.messaging.sending_receiving.attachments.*
|
||||||
import org.session.libsession.utilities.Address
|
import org.session.libsession.utilities.Address
|
||||||
import org.session.libsession.utilities.UploadResult
|
import org.session.libsession.utilities.UploadResult
|
||||||
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
import org.session.libsignal.messages.SignalServiceAttachmentPointer
|
||||||
import org.session.libsignal.messages.SignalServiceAttachmentStream
|
import org.session.libsignal.messages.SignalServiceAttachmentStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@ -18,10 +19,11 @@ interface MessageDataProvider {
|
|||||||
fun getSignalAttachmentStream(attachmentId: Long): SignalServiceAttachmentStream?
|
fun getSignalAttachmentStream(attachmentId: Long): SignalServiceAttachmentStream?
|
||||||
fun getScaledSignalAttachmentStream(attachmentId: Long): SignalServiceAttachmentStream?
|
fun getScaledSignalAttachmentStream(attachmentId: Long): SignalServiceAttachmentStream?
|
||||||
fun getSignalAttachmentPointer(attachmentId: Long): SignalServiceAttachmentPointer?
|
fun getSignalAttachmentPointer(attachmentId: Long): SignalServiceAttachmentPointer?
|
||||||
fun setAttachmentState(attachmentState: AttachmentState, attachmentId: Long, messageID: Long)
|
fun setAttachmentState(attachmentState: AttachmentState, attachmentId: AttachmentId, messageID: Long)
|
||||||
fun insertAttachment(messageId: Long, attachmentId: AttachmentId, stream : InputStream)
|
fun insertAttachment(messageId: Long, attachmentId: AttachmentId, stream : InputStream)
|
||||||
fun updateAudioAttachmentDuration(attachmentId: AttachmentId, durationMs: Long, threadId: Long)
|
fun updateAudioAttachmentDuration(attachmentId: AttachmentId, durationMs: Long, threadId: Long)
|
||||||
fun isOutgoingMessage(timestamp: Long): Boolean
|
fun isMmsOutgoing(mmsMessageId: Long): Boolean
|
||||||
|
fun isOutgoingMessage(mmsId: Long): Boolean
|
||||||
fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult)
|
fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult)
|
||||||
fun handleFailedAttachmentUpload(attachmentId: Long)
|
fun handleFailedAttachmentUpload(attachmentId: Long)
|
||||||
fun getMessageForQuote(timestamp: Long, author: Address): Pair<Long, Boolean>?
|
fun getMessageForQuote(timestamp: Long, author: Address): Pair<Long, Boolean>?
|
||||||
@ -29,4 +31,5 @@ interface MessageDataProvider {
|
|||||||
fun getMessageBodyFor(timestamp: Long, author: String): String
|
fun getMessageBodyFor(timestamp: Long, author: String): String
|
||||||
fun getAttachmentIDsFor(messageID: Long): List<Long>
|
fun getAttachmentIDsFor(messageID: Long): List<Long>
|
||||||
fun getLinkPreviewAttachmentIDFor(messageID: Long): Long?
|
fun getLinkPreviewAttachmentIDFor(messageID: Long): Long?
|
||||||
|
fun getIndividualRecipientForMms(mmsId: Long): Recipient?
|
||||||
}
|
}
|
@ -139,6 +139,7 @@ interface StorageProtocol {
|
|||||||
fun getContactWithSessionID(sessionID: String): Contact?
|
fun getContactWithSessionID(sessionID: String): Contact?
|
||||||
fun getAllContacts(): Set<Contact>
|
fun getAllContacts(): Set<Contact>
|
||||||
fun setContact(contact: Contact)
|
fun setContact(contact: Contact)
|
||||||
|
fun getRecipientForThread(threadId: Long): Recipient?
|
||||||
fun getRecipientSettings(address: Address): RecipientSettings?
|
fun getRecipientSettings(address: Address): RecipientSettings?
|
||||||
fun addContacts(contacts: List<ConfigurationMessage.Contact>)
|
fun addContacts(contacts: List<ConfigurationMessage.Contact>)
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package org.session.libsession.messaging.jobs
|
|||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import org.session.libsession.messaging.MessagingModuleConfiguration
|
import org.session.libsession.messaging.MessagingModuleConfiguration
|
||||||
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
import org.session.libsession.messaging.open_groups.OpenGroupAPIV2
|
||||||
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentState
|
||||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||||
import org.session.libsession.messaging.utilities.Data
|
import org.session.libsession.messaging.utilities.Data
|
||||||
@ -16,6 +17,7 @@ import org.session.libsignal.utilities.Log
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
import java.lang.NullPointerException
|
||||||
|
|
||||||
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) : Job {
|
class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) : Job {
|
||||||
override var delegate: JobDelegate? = null
|
override var delegate: JobDelegate? = null
|
||||||
@ -25,6 +27,9 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
|
|||||||
// Error
|
// Error
|
||||||
internal sealed class Error(val description: String) : Exception(description) {
|
internal sealed class Error(val description: String) : Exception(description) {
|
||||||
object NoAttachment : Error("No such attachment.")
|
object NoAttachment : Error("No such attachment.")
|
||||||
|
object NoThread: Error("Thread no longer exists")
|
||||||
|
object NoSender: Error("Thread recipient or sender does not exist")
|
||||||
|
object DuplicateData: Error("Attachment already downloaded")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
@ -41,22 +46,56 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
|
|||||||
override fun execute() {
|
override fun execute() {
|
||||||
val storage = MessagingModuleConfiguration.shared.storage
|
val storage = MessagingModuleConfiguration.shared.storage
|
||||||
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
val messageDataProvider = MessagingModuleConfiguration.shared.messageDataProvider
|
||||||
val handleFailure: (java.lang.Exception) -> Unit = { exception ->
|
val threadID = storage.getThreadIdForMms(databaseMessageID)
|
||||||
|
|
||||||
|
val handleFailure: (java.lang.Exception, attachmentId: AttachmentId?) -> Unit = { exception, attachment ->
|
||||||
if (exception == Error.NoAttachment
|
if (exception == Error.NoAttachment
|
||||||
|
|| exception == Error.NoThread
|
||||||
|
|| exception == Error.NoSender
|
||||||
|| (exception is OnionRequestAPI.HTTPRequestFailedAtDestinationException && exception.statusCode == 400)) {
|
|| (exception is OnionRequestAPI.HTTPRequestFailedAtDestinationException && exception.statusCode == 400)) {
|
||||||
messageDataProvider.setAttachmentState(AttachmentState.FAILED, attachmentID, databaseMessageID)
|
attachment?.let { id ->
|
||||||
|
messageDataProvider.setAttachmentState(AttachmentState.FAILED, id, databaseMessageID)
|
||||||
|
} ?: run {
|
||||||
|
messageDataProvider.setAttachmentState(AttachmentState.FAILED, AttachmentId(attachmentID,0), databaseMessageID)
|
||||||
|
}
|
||||||
this.handlePermanentFailure(exception)
|
this.handlePermanentFailure(exception)
|
||||||
} else {
|
} else {
|
||||||
this.handleFailure(exception)
|
this.handleFailure(exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (threadID < 0) {
|
||||||
|
handleFailure(Error.NoThread, null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val threadRecipient = storage.getRecipientForThread(threadID)
|
||||||
|
val sender = if (messageDataProvider.isMmsOutgoing(databaseMessageID)) {
|
||||||
|
storage.getUserPublicKey()
|
||||||
|
} else {
|
||||||
|
messageDataProvider.getIndividualRecipientForMms(databaseMessageID)?.address?.serialize()
|
||||||
|
}
|
||||||
|
val contact = sender?.let { storage.getContactWithSessionID(it) }
|
||||||
|
if (threadRecipient == null || sender == null || contact == null) {
|
||||||
|
handleFailure(Error.NoSender, null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!threadRecipient.isGroupRecipient && (!contact.isTrusted && storage.getUserPublicKey() != sender)) {
|
||||||
|
// if we aren't receiving a group message, a message from ourselves (self-send) and the contact sending is not trusted:
|
||||||
|
// do not continue, but do not fail
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var tempFile: File? = null
|
var tempFile: File? = null
|
||||||
try {
|
try {
|
||||||
val attachment = messageDataProvider.getDatabaseAttachment(attachmentID)
|
val attachment = messageDataProvider.getDatabaseAttachment(attachmentID)
|
||||||
?: return handleFailure(Error.NoAttachment)
|
?: return handleFailure(Error.NoAttachment, null)
|
||||||
messageDataProvider.setAttachmentState(AttachmentState.STARTED, attachmentID, this.databaseMessageID)
|
if (attachment.hasData()) {
|
||||||
|
handleFailure(Error.DuplicateData, attachment.attachmentId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
messageDataProvider.setAttachmentState(AttachmentState.STARTED, attachment.attachmentId, this.databaseMessageID)
|
||||||
tempFile = createTempFile()
|
tempFile = createTempFile()
|
||||||
val threadID = storage.getThreadIdForMms(databaseMessageID)
|
|
||||||
val openGroupV2 = storage.getV2OpenGroup(threadID)
|
val openGroupV2 = storage.getV2OpenGroup(threadID)
|
||||||
if (openGroupV2 == null) {
|
if (openGroupV2 == null) {
|
||||||
DownloadUtilities.downloadFile(tempFile, attachment.url)
|
DownloadUtilities.downloadFile(tempFile, attachment.url)
|
||||||
@ -89,7 +128,7 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long)
|
|||||||
handleSuccess()
|
handleSuccess()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
tempFile?.delete()
|
tempFile?.delete()
|
||||||
return handleFailure(e)
|
return handleFailure(e,null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ class JobQueue : JobDelegate {
|
|||||||
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
|
private val jobTimestampMap = ConcurrentHashMap<Long, AtomicInteger>()
|
||||||
private val rxDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
private val rxDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
private val txDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
private val txDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||||
private val attachmentDispatcher = Executors.newFixedThreadPool(2).asCoroutineDispatcher()
|
private val attachmentDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
|
||||||
private val scope = GlobalScope + SupervisorJob()
|
private val scope = GlobalScope + SupervisorJob()
|
||||||
private val queue = Channel<Job>(UNLIMITED)
|
private val queue = Channel<Job>(UNLIMITED)
|
||||||
private val pendingJobIds = mutableSetOf<String>()
|
private val pendingJobIds = mutableSetOf<String>()
|
||||||
@ -100,6 +100,23 @@ class JobQueue : JobDelegate {
|
|||||||
Log.d("Loki", "resumed pending send message $id")
|
Log.d("Loki", "resumed pending send message $id")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun resumePendingJobs(typeKey: String) {
|
||||||
|
val allPendingJobs = MessagingModuleConfiguration.shared.storage.getAllPendingJobs(typeKey)
|
||||||
|
val pendingJobs = mutableListOf<Job>()
|
||||||
|
for ((id, job) in allPendingJobs) {
|
||||||
|
if (job == null) {
|
||||||
|
// Job failed to deserialize, remove it from the DB
|
||||||
|
handleJobFailedPermanently(id)
|
||||||
|
} else {
|
||||||
|
pendingJobs.add(job)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pendingJobs.sortedBy { it.id }.forEach { job ->
|
||||||
|
Log.i("Loki", "Resuming pending job of type: ${job::class.simpleName}.")
|
||||||
|
queue.offer(job) // Offer always called on unlimited capacity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun resumePendingJobs() {
|
fun resumePendingJobs() {
|
||||||
if (hasResumedPendingJobs) {
|
if (hasResumedPendingJobs) {
|
||||||
Log.d("Loki", "resumePendingJobs() should only be called once.")
|
Log.d("Loki", "resumePendingJobs() should only be called once.")
|
||||||
@ -114,20 +131,7 @@ class JobQueue : JobDelegate {
|
|||||||
NotifyPNServerJob.KEY
|
NotifyPNServerJob.KEY
|
||||||
)
|
)
|
||||||
allJobTypes.forEach { type ->
|
allJobTypes.forEach { type ->
|
||||||
val allPendingJobs = MessagingModuleConfiguration.shared.storage.getAllPendingJobs(type)
|
resumePendingJobs(type)
|
||||||
val pendingJobs = mutableListOf<Job>()
|
|
||||||
for ((id, job) in allPendingJobs) {
|
|
||||||
if (job == null) {
|
|
||||||
// Job failed to deserialize, remove it from the DB
|
|
||||||
handleJobFailedPermanently(id)
|
|
||||||
} else {
|
|
||||||
pendingJobs.add(job)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pendingJobs.sortedBy { it.id }.forEach { job ->
|
|
||||||
Log.i("Loki", "Resuming pending job of type: ${job::class.simpleName}.")
|
|
||||||
queue.offer(job) // Offer always called on unlimited capacity
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user