feat: trust dialog and processing attachments for users after trusting them

This commit is contained in:
jubb
2021-07-09 15:13:43 +10:00
parent bc4f660fb0
commit 9dfd051e63
18 changed files with 230 additions and 59 deletions

View File

@@ -9,6 +9,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.*
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.UploadResult
import org.session.libsession.utilities.Util
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.messages.SignalServiceAttachment
import org.session.libsignal.messages.SignalServiceAttachmentPointer
import org.session.libsignal.messages.SignalServiceAttachmentStream
@@ -92,6 +93,14 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper)
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) {
val attachmentDatabase = DatabaseFactory.getAttachmentDatabase(context)
attachmentDatabase.insertAttachmentsForPlaceholder(messageId, attachmentId, stream)

View File

@@ -89,6 +89,7 @@ import org.thoughtcrime.securesms.conversation.v2.messages.VisibleMessageView
import org.thoughtcrime.securesms.conversation.v2.search.SearchBottomBar
import org.thoughtcrime.securesms.conversation.v2.search.SearchViewModel
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.DraftDatabase
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts
@@ -255,6 +256,10 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
push(intent, false)
}
override fun showDialog(baseDialog: BaseDialog, tag: String?) {
baseDialog.show(supportFragmentManager, tag)
}
private fun setUpRecyclerView() {
conversationRecyclerView.adapter = adapter
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, true)

View File

@@ -15,6 +15,7 @@ import kotlinx.android.synthetic.main.album_thumbnail_view.view.*
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.recipients.Recipient
@@ -86,7 +87,7 @@ class AlbumThumbnailView : FrameLayout {
// hit intersects with this particular child
val slide = slides.getOrNull(index) ?: return
// only open to downloaded images
if (slide.isPendingDownload) {
if (slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED) {
// restart download here
(slide.asAttachment() as? DatabaseAttachment)?.let { attachment ->
val attachmentId = attachment.attachmentId.rowId

View File

@@ -9,6 +9,7 @@ import androidx.appcompat.app.AlertDialog
import kotlinx.android.synthetic.main.dialog_download.view.*
import network.loki.messenger.R
import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
import org.thoughtcrime.securesms.database.DatabaseFactory
@@ -36,6 +37,12 @@ class DownloadDialog(private val recipient: Recipient) : BaseDialog() {
}
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()
dismiss()
}
}

View File

@@ -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.loki.utilities.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))
}
}

View File

@@ -61,7 +61,7 @@ class VisibleMessageContentView : LinearLayout {
// region Updating
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
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
@@ -106,30 +106,54 @@ class VisibleMessageContentView : LinearLayout {
}
}
} else if (message is MmsMessageRecord && message.slideDeck.audioSlide != null) {
val voiceMessageView = VoiceMessageView(context)
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
mainContainer.addView(voiceMessageView)
// We have to use onContentClick (rather than a click listener directly on the voice
// message view) so as to not interfere with all the other gestures.
onContentClick = { voiceMessageView.togglePlayback() }
onContentDoubleTap = { voiceMessageView.handleDoubleTap() }
// Audio attachment
if (contactIsTrusted || message.isOutgoing) {
val voiceMessageView = VoiceMessageView(context)
voiceMessageView.bind(message, isStartOfMessageCluster, isEndOfMessageCluster)
mainContainer.addView(voiceMessageView)
// We have to use onContentClick (rather than a click listener directly on the voice
// 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) {
val documentView = DocumentView(context)
documentView.bind(message, VisibleMessageContentView.getTextColor(context, message))
mainContainer.addView(documentView)
// Document attachment
if (contactIsTrusted || message.isOutgoing) {
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()) {
val albumThumbnailView = AlbumThumbnailView(context)
mainContainer.addView(albumThumbnailView)
// 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
albumThumbnailView.bind(
glideRequests = glide,
message = message,
isStart = isStartOfMessageCluster,
isEnd = isEndOfMessageCluster
)
onContentClick = { event ->
albumThumbnailView.calculateHitObject(event, message, thread)
// Images/Video attachment
if (contactIsTrusted || message.isOutgoing) {
val albumThumbnailView = AlbumThumbnailView(context)
mainContainer.addView(albumThumbnailView)
// 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
albumThumbnailView.bind(
glideRequests = glide,
message = message,
isStart = isStartOfMessageCluster,
isEnd = isEndOfMessageCluster
)
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) {
val openGroupInvitationView = OpenGroupInvitationView(context)

View File

@@ -84,6 +84,7 @@ class VisibleMessageView : LinearLayout {
val threadDB = DatabaseFactory.getThreadDatabase(context)
val thread = threadDB.getRecipientForThreadId(threadID)!!
val contactDB = DatabaseFactory.getSessionContactDatabase(context)
val contact = contactDB.getContactWithSessionID(senderSessionID)
val isGroupThread = thread.isGroupRecipient
val isStartOfMessageCluster = isStartOfMessageCluster(message, previous, isGroupThread)
val isEndOfMessageCluster = isEndOfMessageCluster(message, next, isGroupThread)
@@ -103,7 +104,7 @@ class VisibleMessageView : LinearLayout {
}
senderNameTextView.isVisible = isStartOfMessageCluster
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 {
profilePictureContainer.visibility = View.GONE
senderNameTextView.visibility = View.GONE
@@ -151,7 +152,7 @@ class VisibleMessageView : LinearLayout {
var maxWidth = screenWidth - startPadding - endPadding
if (profilePictureContainer.visibility != View.GONE) { maxWidth -= profilePictureContainer.width }
// Populate content view
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
onDoubleTap = { messageContentView.onContentDoubleTap?.invoke() }
}

View File

@@ -109,8 +109,8 @@ open class KThumbnailView: FrameLayout {
this.slide = slide
loadIndicator.isVisible = slide.isInProgress && !slide.isPendingDownload
downloadIndicator.isVisible = slide.isPendingDownload
loadIndicator.isVisible = slide.isInProgress
downloadIndicator.isVisible = slide.transferState == AttachmentTransferProgress.TRANSFER_PROGRESS_FAILED
dimensDelegate.setDimens(naturalWidth, naturalHeight)
invalidate()

View File

@@ -505,9 +505,9 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
val mmsDb = DatabaseFactory.getMmsDatabase(context)
val cursor = mmsDb.getMessage(mmsId)
val reader = mmsDb.readerFor(cursor)
val threadId = reader.next.threadId
val threadId = reader.next?.threadId
cursor.close()
return threadId
return threadId ?: -1
}
override fun getContactWithSessionID(sessionID: String): Contact? {
@@ -522,6 +522,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
DatabaseFactory.getSessionContactDatabase(context).setContact(contact)
}
override fun getRecipientForThread(threadId: Long): Recipient? {
return DatabaseFactory.getThreadDatabase(context).getRecipientForThreadId(threadId)
}
override fun getRecipientSettings(address: Address): Recipient.RecipientSettings? {
val recipientSettings = DatabaseFactory.getRecipientDatabase(context).getRecipientSettings(address)
return if (recipientSettings.isPresent) { recipientSettings.get() } else null

View File

@@ -47,11 +47,14 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
}.toSet()
}
fun setContactIsTrusted(contact: Contact, isTrusted: Boolean) {
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.insertOrUpdate(sessionContactTable, contentValues, "$sessionID = ?", arrayOf( contact.sessionID ))
database.update(sessionContactTable, contentValues, "$sessionID = ?", arrayOf( contact.sessionID ))
if (threadID >= 0) {
notifyConversationListeners(threadID)
}
notifyConversationListListeners()
}
@@ -66,7 +69,7 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
contact.profilePictureEncryptionKey?.let {
contentValues.put(profilePictureEncryptionKey, Base64.encodeBytes(it))
}
contentValues.put(threadID, threadID)
contentValues.put(threadID, contact.threadID)
contentValues.put(isTrusted, if (contact.isTrusted) 1 else 0)
database.insertOrUpdate(sessionContactTable, contentValues, "$sessionID = ?", arrayOf( contact.sessionID ))
notifyConversationListListeners()

View File

@@ -10,6 +10,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import network.loki.messenger.R
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
fun BaseActionBarActivity.setUpActionBarSessionLogo(hideBackButton: Boolean = false) {
val actionbar = supportActionBar!!
@@ -65,4 +66,5 @@ interface ActivityDispatcher {
fun get(context: Context) = context.getSystemService(SERVICE) as? ActivityDispatcher
}
fun dispatchIntent(body: (Context)->Intent?)
fun showDialog(baseDialog: BaseDialog, tag: String? = null)
}

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

View File

@@ -874,4 +874,6 @@
<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="media">Media</string>
<string name="UntrustedAttachmentView_download_attachment">Tap to download %s</string>
</resources>