diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 0b99be2529..363fcab014 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -613,6 +613,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // region Interaction override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + return false + } return ConversationMenuHelper.onOptionItemSelected(this, item, thread) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt index d4b1720860..a56392b301 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/LinkPreviewView.kt @@ -1,23 +1,20 @@ package org.thoughtcrime.securesms.conversation.v2.messages import android.content.Context -import android.content.Intent import android.graphics.Canvas -import android.graphics.drawable.Drawable -import android.net.Uri +import android.graphics.Rect +import android.text.method.LinkMovementMethod import android.util.AttributeSet import android.view.LayoutInflater -import android.view.ViewOutlineProvider import android.widget.LinearLayout -import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat +import androidx.core.view.isVisible import kotlinx.android.synthetic.main.view_link_preview.view.* import network.loki.messenger.R import org.thoughtcrime.securesms.components.CornerMask import org.thoughtcrime.securesms.conversation.v2.dialogs.OpenURLDialog import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities -import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities import org.thoughtcrime.securesms.mms.GlideRequests @@ -44,7 +41,8 @@ class LinkPreviewView : LinearLayout { // Thumbnail if (linkPreview.getThumbnail().isPresent) { // This internally fetches the thumbnail - thumbnailImageView.setImageResource(glide, ImageSlide(context, linkPreview.getThumbnail().get()), false, false) + thumbnailImageView.setImageResource(glide, ImageSlide(context, linkPreview.getThumbnail().get()), isPreview = false) + thumbnailImageView.loadIndicator.isVisible = false } // Title titleTextView.text = linkPreview.title @@ -72,6 +70,14 @@ class LinkPreviewView : LinearLayout { // endregion // region Interaction + fun calculateHit(hitRect: Rect) { + val previewRect = Rect() + mainLinkPreviewParent.getGlobalVisibleRect(previewRect) + if (previewRect.contains(hitRect)) { + openURL() + } + } + fun openURL() { val url = this.url ?: return val activity = context as AppCompatActivity diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index b0ef5d4672..3067ac9ecc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -6,18 +6,21 @@ import android.graphics.Rect import android.graphics.drawable.Drawable import android.text.style.BackgroundColorSpan import android.text.style.ForegroundColorSpan +import android.text.method.LinkMovementMethod +import android.text.style.URLSpan import android.text.util.Linkify import android.util.AttributeSet import android.util.TypedValue import android.view.LayoutInflater import android.widget.LinearLayout import android.widget.TextView -import android.widget.Toast import androidx.annotation.ColorInt import androidx.annotation.DrawableRes +import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeCompat +import androidx.core.text.getSpans import androidx.core.text.toSpannable import kotlinx.android.synthetic.main.view_visible_message_content.view.* import network.loki.messenger.R @@ -26,6 +29,8 @@ import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView import org.thoughtcrime.securesms.components.emoji.EmojiTextView +import org.thoughtcrime.securesms.conversation.v2.dialogs.OpenURLDialog +import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.loki.utilities.* @@ -67,7 +72,9 @@ class VisibleMessageContentView : LinearLayout { val linkPreviewView = LinkPreviewView(context) linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster, searchQuery) mainContainer.addView(linkPreviewView) - onContentClick = { linkPreviewView.openURL() } + onContentClick = { rect -> + linkPreviewView.calculateHit(rect) + } // Body text view is inside the link preview for layout convenience } else if (message is MmsMessageRecord && message.quote != null) { val quote = message.quote!! @@ -92,7 +99,7 @@ class VisibleMessageContentView : LinearLayout { onContentDoubleTap = { voiceMessageView.handleDoubleTap() } } else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) { val documentView = DocumentView(context) - documentView.bind(message, VisibleMessageContentView.getTextColor(context, message)) + documentView.bind(message, getTextColor(context, message)) mainContainer.addView(documentView) } else if (message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty()) { val albumThumbnailView = AlbumThumbnailView(context) @@ -108,13 +115,12 @@ class VisibleMessageContentView : LinearLayout { onContentClick = { albumThumbnailView.calculateHitObject(it, message) } } else if (message.isOpenGroupInvitation) { val openGroupInvitationView = OpenGroupInvitationView(context) - openGroupInvitationView.bind(message, VisibleMessageContentView.getTextColor(context, message)) + openGroupInvitationView.bind(message, getTextColor(context, message)) mainContainer.addView(openGroupInvitationView) onContentClick = { openGroupInvitationView.joinOpenGroup() } } else { - val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message, searchQuery) + val bodyTextView = getBodyTextView(context, message, searchQuery) mainContainer.addView(bodyTextView) - onContentClick = { openURLIfNeeded(message) } } } @@ -138,12 +144,6 @@ class VisibleMessageContentView : LinearLayout { } // endregion - // region Interaction - private fun openURLIfNeeded(message: MessageRecord) { - Toast.makeText(context, "Not yet implemented", Toast.LENGTH_LONG).show() - } - // endregion - // region Convenience companion object { @@ -158,10 +158,25 @@ class VisibleMessageContentView : LinearLayout { result.setLinkTextColor(color) var body = message.body.toSpannable() Linkify.addLinks(body, Linkify.WEB_URLS) + + // replace URLSpans with ModalURLSpans + body.getSpans(0, body.length).toList().forEach { urlSpan -> + val replacementSpan = ModalURLSpan(urlSpan.url) { url -> + val activity = context as AppCompatActivity + OpenURLDialog(url).show(activity.supportFragmentManager, "Open URL Dialog") + } + val start = body.getSpanStart(urlSpan) + val end = body.getSpanEnd(urlSpan) + val flags = body.getSpanFlags(urlSpan) + body.removeSpan(urlSpan) + body.setSpan(replacementSpan, start, end, flags) + } + body = MentionUtilities.highlightMentions(body, message.isOutgoing, message.threadId, context); body = SearchUtil.getHighlightedSpan(Locale.getDefault(), StyleFactory { BackgroundColorSpan(Color.WHITE) }, body, searchQuery) body = SearchUtil.getHighlightedSpan(Locale.getDefault(), StyleFactory { ForegroundColorSpan(Color.BLACK) }, body, searchQuery) result.text = body + result.movementMethod = LinkMovementMethod.getInstance() return result } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt index b070264944..89045be665 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/KThumbnailView.kt @@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget import org.thoughtcrime.securesms.mms.* import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri +import org.thoughtcrime.securesms.util.MediaUtil open class KThumbnailView: FrameLayout { @@ -38,7 +39,6 @@ open class KThumbnailView: FrameLayout { private val image by lazy { thumbnail_image } private val playOverlay by lazy { play_overlay } - private val captionIcon by lazy { thumbnail_caption_icon } val loadIndicator: View by lazy { thumbnail_load_indicator } private val dimensDelegate = ThumbnailDimensDelegate() @@ -110,7 +110,6 @@ open class KThumbnailView: FrameLayout { this.slide = slide - captionIcon.isVisible = slide.caption.isPresent loadIndicator.isVisible = slide.isInProgress dimensDelegate.setDimens(naturalWidth, naturalHeight) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ModalURLSpan.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ModalURLSpan.kt new file mode 100644 index 0000000000..358dd56357 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ModalURLSpan.kt @@ -0,0 +1,10 @@ +package org.thoughtcrime.securesms.conversation.v2.utilities + +import android.text.style.URLSpan +import android.view.View + +class ModalURLSpan(url: String, private val openModalCallback: (String)->Unit): URLSpan(url) { + override fun onClick(widget: View) { + openModalCallback(url) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.java index 79d0099998..f40a57924a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/utilities/ThumbnailView.java @@ -57,7 +57,6 @@ public class ThumbnailView extends FrameLayout { private ImageView image; private View playOverlay; - private View captionIcon; private View loadIndicator; private OnClickListener parentClickListener; @@ -87,7 +86,6 @@ public class ThumbnailView extends FrameLayout { this.image = findViewById(R.id.thumbnail_image); this.playOverlay = findViewById(R.id.play_overlay); - this.captionIcon = findViewById(R.id.thumbnail_caption_icon); this.loadIndicator = findViewById(R.id.thumbnail_load_indicator); super.setOnClickListener(new ThumbnailClickDispatcher()); @@ -278,8 +276,6 @@ public class ThumbnailView extends FrameLayout { this.slide = slide; - this.captionIcon.setVisibility(GONE); - dimens[WIDTH] = naturalWidth; dimens[HEIGHT] = naturalHeight; invalidate(); diff --git a/app/src/main/res/layout/thumbnail_view.xml b/app/src/main/res/layout/thumbnail_view.xml index b20b58356d..8922112823 100644 --- a/app/src/main/res/layout/thumbnail_view.xml +++ b/app/src/main/res/layout/thumbnail_view.xml @@ -14,14 +14,6 @@ android:scaleType="fitCenter" android:contentDescription="@string/conversation_item__mms_image_description" /> - - - , snode: Snode, publicKey: String? = null): Promise, Exception> { val payload = mapOf( "method" to method.rawValue, "params" to parameters ) return sendOnionRequest(Destination.Snode(snode), payload).recover { exception -> - val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException - if (httpRequestFailedException != null) { - val error = SnodeAPI.handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey) - if (error != null) { throw error } + val error = when (exception) { + is HTTP.HTTPRequestFailedException -> SnodeAPI.handleSnodeError(exception.statusCode, exception.json, snode, publicKey) + is HTTPRequestFailedAtDestinationException -> SnodeAPI.handleSnodeError(exception.statusCode, exception.json, snode, publicKey) + else -> null } + if (error != null) { throw error } throw exception } } diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 281bd906e7..bec77506eb 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -290,9 +290,7 @@ object SnodeAPI { getTargetSnodes(destination).map { swarm -> swarm.map { snode -> val parameters = message.toJSON() - retryIfNeeded(maxRetryCount) { - invoke(Snode.Method.SendMessage, snode, destination, parameters) - } + invoke(Snode.Method.SendMessage, snode, destination, parameters) }.toSet() } }