This commit is contained in:
Ryan Zhao 2021-06-29 16:15:25 +10:00
commit 19d683082e
11 changed files with 64 additions and 42 deletions

View File

@ -613,6 +613,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// region Interaction // region Interaction
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
return false
}
return ConversationMenuHelper.onOptionItemSelected(this, item, thread) return ConversationMenuHelper.onOptionItemSelected(this, item, thread)
} }

View File

@ -1,23 +1,20 @@
package org.thoughtcrime.securesms.conversation.v2.messages package org.thoughtcrime.securesms.conversation.v2.messages
import android.content.Context import android.content.Context
import android.content.Intent
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.drawable.Drawable import android.graphics.Rect
import android.net.Uri import android.text.method.LinkMovementMethod
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewOutlineProvider
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.view_link_preview.view.* import kotlinx.android.synthetic.main.view_link_preview.view.*
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.components.CornerMask import org.thoughtcrime.securesms.components.CornerMask
import org.thoughtcrime.securesms.conversation.v2.dialogs.OpenURLDialog import org.thoughtcrime.securesms.conversation.v2.dialogs.OpenURLDialog
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities 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.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities import org.thoughtcrime.securesms.loki.utilities.UiModeUtilities
import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.GlideRequests
@ -44,7 +41,8 @@ class LinkPreviewView : LinearLayout {
// Thumbnail // Thumbnail
if (linkPreview.getThumbnail().isPresent) { if (linkPreview.getThumbnail().isPresent) {
// This internally fetches the thumbnail // 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 // Title
titleTextView.text = linkPreview.title titleTextView.text = linkPreview.title
@ -72,6 +70,14 @@ class LinkPreviewView : LinearLayout {
// endregion // endregion
// region Interaction // region Interaction
fun calculateHit(hitRect: Rect) {
val previewRect = Rect()
mainLinkPreviewParent.getGlobalVisibleRect(previewRect)
if (previewRect.contains(hitRect)) {
openURL()
}
}
fun openURL() { fun openURL() {
val url = this.url ?: return val url = this.url ?: return
val activity = context as AppCompatActivity val activity = context as AppCompatActivity

View File

@ -6,18 +6,21 @@ import android.graphics.Rect
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.text.style.BackgroundColorSpan import android.text.style.BackgroundColorSpan
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.text.method.LinkMovementMethod
import android.text.style.URLSpan
import android.text.util.Linkify import android.text.util.Linkify
import android.util.AttributeSet import android.util.AttributeSet
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.BlendModeColorFilterCompat import androidx.core.graphics.BlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat import androidx.core.graphics.BlendModeCompat
import androidx.core.text.getSpans
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
import kotlinx.android.synthetic.main.view_visible_message_content.view.* import kotlinx.android.synthetic.main.view_visible_message_content.view.*
import network.loki.messenger.R import network.loki.messenger.R
@ -26,6 +29,8 @@ import org.session.libsession.utilities.ViewUtil
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView import org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView
import org.thoughtcrime.securesms.components.emoji.EmojiTextView 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.MessageRecord
import org.thoughtcrime.securesms.database.model.MmsMessageRecord import org.thoughtcrime.securesms.database.model.MmsMessageRecord
import org.thoughtcrime.securesms.loki.utilities.* import org.thoughtcrime.securesms.loki.utilities.*
@ -67,7 +72,9 @@ class VisibleMessageContentView : LinearLayout {
val linkPreviewView = LinkPreviewView(context) val linkPreviewView = LinkPreviewView(context)
linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster, searchQuery) linkPreviewView.bind(message, glide, isStartOfMessageCluster, isEndOfMessageCluster, searchQuery)
mainContainer.addView(linkPreviewView) mainContainer.addView(linkPreviewView)
onContentClick = { linkPreviewView.openURL() } onContentClick = { rect ->
linkPreviewView.calculateHit(rect)
}
// Body text view is inside the link preview for layout convenience // Body text view is inside the link preview for layout convenience
} else if (message is MmsMessageRecord && message.quote != null) { } else if (message is MmsMessageRecord && message.quote != null) {
val quote = message.quote!! val quote = message.quote!!
@ -92,7 +99,7 @@ class VisibleMessageContentView : LinearLayout {
onContentDoubleTap = { voiceMessageView.handleDoubleTap() } onContentDoubleTap = { voiceMessageView.handleDoubleTap() }
} else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) { } else if (message is MmsMessageRecord && message.slideDeck.documentSlide != null) {
val documentView = DocumentView(context) val documentView = DocumentView(context)
documentView.bind(message, VisibleMessageContentView.getTextColor(context, message)) documentView.bind(message, getTextColor(context, message))
mainContainer.addView(documentView) mainContainer.addView(documentView)
} else if (message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty()) { } else if (message is MmsMessageRecord && message.slideDeck.asAttachments().isNotEmpty()) {
val albumThumbnailView = AlbumThumbnailView(context) val albumThumbnailView = AlbumThumbnailView(context)
@ -108,13 +115,12 @@ class VisibleMessageContentView : LinearLayout {
onContentClick = { albumThumbnailView.calculateHitObject(it, message) } onContentClick = { albumThumbnailView.calculateHitObject(it, message) }
} else if (message.isOpenGroupInvitation) { } else if (message.isOpenGroupInvitation) {
val openGroupInvitationView = OpenGroupInvitationView(context) val openGroupInvitationView = OpenGroupInvitationView(context)
openGroupInvitationView.bind(message, VisibleMessageContentView.getTextColor(context, message)) openGroupInvitationView.bind(message, getTextColor(context, message))
mainContainer.addView(openGroupInvitationView) mainContainer.addView(openGroupInvitationView)
onContentClick = { openGroupInvitationView.joinOpenGroup() } onContentClick = { openGroupInvitationView.joinOpenGroup() }
} else { } else {
val bodyTextView = VisibleMessageContentView.getBodyTextView(context, message, searchQuery) val bodyTextView = getBodyTextView(context, message, searchQuery)
mainContainer.addView(bodyTextView) mainContainer.addView(bodyTextView)
onContentClick = { openURLIfNeeded(message) }
} }
} }
@ -138,12 +144,6 @@ class VisibleMessageContentView : LinearLayout {
} }
// endregion // endregion
// region Interaction
private fun openURLIfNeeded(message: MessageRecord) {
Toast.makeText(context, "Not yet implemented", Toast.LENGTH_LONG).show()
}
// endregion
// region Convenience // region Convenience
companion object { companion object {
@ -158,10 +158,25 @@ class VisibleMessageContentView : LinearLayout {
result.setLinkTextColor(color) result.setLinkTextColor(color)
var body = message.body.toSpannable() var body = message.body.toSpannable()
Linkify.addLinks(body, Linkify.WEB_URLS) Linkify.addLinks(body, Linkify.WEB_URLS)
// replace URLSpans with ModalURLSpans
body.getSpans<URLSpan>(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 = 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 { BackgroundColorSpan(Color.WHITE) }, body, searchQuery)
body = SearchUtil.getHighlightedSpan(Locale.getDefault(), StyleFactory { ForegroundColorSpan(Color.BLACK) }, body, searchQuery) body = SearchUtil.getHighlightedSpan(Locale.getDefault(), StyleFactory { ForegroundColorSpan(Color.BLACK) }, body, searchQuery)
result.text = body result.text = body
result.movementMethod = LinkMovementMethod.getInstance()
return result return result
} }

View File

@ -23,6 +23,7 @@ import org.thoughtcrime.securesms.components.GlideBitmapListeningTarget
import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget import org.thoughtcrime.securesms.components.GlideDrawableListeningTarget
import org.thoughtcrime.securesms.mms.* import org.thoughtcrime.securesms.mms.*
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri
import org.thoughtcrime.securesms.util.MediaUtil
open class KThumbnailView: FrameLayout { open class KThumbnailView: FrameLayout {
@ -38,7 +39,6 @@ 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 }
private val captionIcon by lazy { thumbnail_caption_icon }
val loadIndicator: View by lazy { thumbnail_load_indicator } val loadIndicator: View by lazy { thumbnail_load_indicator }
private val dimensDelegate = ThumbnailDimensDelegate() private val dimensDelegate = ThumbnailDimensDelegate()
@ -110,7 +110,6 @@ open class KThumbnailView: FrameLayout {
this.slide = slide this.slide = slide
captionIcon.isVisible = slide.caption.isPresent
loadIndicator.isVisible = slide.isInProgress loadIndicator.isVisible = slide.isInProgress
dimensDelegate.setDimens(naturalWidth, naturalHeight) dimensDelegate.setDimens(naturalWidth, naturalHeight)

View File

@ -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)
}
}

View File

@ -57,7 +57,6 @@ public class ThumbnailView extends FrameLayout {
private ImageView image; private ImageView image;
private View playOverlay; private View playOverlay;
private View captionIcon;
private View loadIndicator; private View loadIndicator;
private OnClickListener parentClickListener; private OnClickListener parentClickListener;
@ -87,7 +86,6 @@ public class ThumbnailView extends FrameLayout {
this.image = findViewById(R.id.thumbnail_image); this.image = findViewById(R.id.thumbnail_image);
this.playOverlay = findViewById(R.id.play_overlay); this.playOverlay = findViewById(R.id.play_overlay);
this.captionIcon = findViewById(R.id.thumbnail_caption_icon);
this.loadIndicator = findViewById(R.id.thumbnail_load_indicator); this.loadIndicator = findViewById(R.id.thumbnail_load_indicator);
super.setOnClickListener(new ThumbnailClickDispatcher()); super.setOnClickListener(new ThumbnailClickDispatcher());
@ -278,8 +276,6 @@ public class ThumbnailView extends FrameLayout {
this.slide = slide; this.slide = slide;
this.captionIcon.setVisibility(GONE);
dimens[WIDTH] = naturalWidth; dimens[WIDTH] = naturalWidth;
dimens[HEIGHT] = naturalHeight; dimens[HEIGHT] = naturalHeight;
invalidate(); invalidate();

View File

@ -14,14 +14,6 @@
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:contentDescription="@string/conversation_item__mms_image_description" /> android:contentDescription="@string/conversation_item__mms_image_description" />
<ImageView
android:id="@+id/thumbnail_caption_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="6dp"
android:src="@drawable/ic_caption_28"
android:visibility="gone" />
<org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailProgressBar <org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailProgressBar
android:id="@+id/thumbnail_load_indicator" android:id="@+id/thumbnail_load_indicator"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -8,6 +8,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout <LinearLayout
android:id="@+id/mainLinkPreviewParent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
@ -24,7 +25,7 @@
android:src="@drawable/ic_link" android:src="@drawable/ic_link"
app:tint="@color/text" /> app:tint="@color/text" />
<org.thoughtcrime.securesms.conversation.v2.utilities.ThumbnailView <org.thoughtcrime.securesms.conversation.v2.utilities.KThumbnailView
android:id="@+id/thumbnailImageView" android:id="@+id/thumbnailImageView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"

View File

@ -7,6 +7,7 @@ import nl.komponents.kovenant.functional.map
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import org.session.libsession.messaging.jobs.Job.Companion.MAX_BUFFER_SIZE
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI
import org.session.libsession.messaging.utilities.Data import org.session.libsession.messaging.utilities.Data
@ -64,7 +65,7 @@ class NotifyPNServerJob(val message: SnodeMessage) : Job {
val kryo = Kryo() val kryo = Kryo()
kryo.isRegistrationRequired = false kryo.isRegistrationRequired = false
val serializedMessage = ByteArray(4096) val serializedMessage = ByteArray(4096)
val output = Output(serializedMessage) val output = Output(serializedMessage, MAX_BUFFER_SIZE)
kryo.writeObject(output, message) kryo.writeObject(output, message)
output.close() output.close()
return Data.Builder() return Data.Builder()

View File

@ -433,11 +433,12 @@ object OnionRequestAPI {
internal fun sendOnionRequest(method: Snode.Method, parameters: Map<*, *>, snode: Snode, publicKey: String? = null): Promise<Map<*, *>, Exception> { internal fun sendOnionRequest(method: Snode.Method, parameters: Map<*, *>, snode: Snode, publicKey: String? = null): Promise<Map<*, *>, Exception> {
val payload = mapOf( "method" to method.rawValue, "params" to parameters ) val payload = mapOf( "method" to method.rawValue, "params" to parameters )
return sendOnionRequest(Destination.Snode(snode), payload).recover { exception -> return sendOnionRequest(Destination.Snode(snode), payload).recover { exception ->
val httpRequestFailedException = exception as? HTTP.HTTPRequestFailedException val error = when (exception) {
if (httpRequestFailedException != null) { is HTTP.HTTPRequestFailedException -> SnodeAPI.handleSnodeError(exception.statusCode, exception.json, snode, publicKey)
val error = SnodeAPI.handleSnodeError(httpRequestFailedException.statusCode, httpRequestFailedException.json, snode, publicKey) is HTTPRequestFailedAtDestinationException -> SnodeAPI.handleSnodeError(exception.statusCode, exception.json, snode, publicKey)
if (error != null) { throw error } else -> null
} }
if (error != null) { throw error }
throw exception throw exception
} }
} }

View File

@ -290,9 +290,7 @@ object SnodeAPI {
getTargetSnodes(destination).map { swarm -> getTargetSnodes(destination).map { swarm ->
swarm.map { snode -> swarm.map { snode ->
val parameters = message.toJSON() val parameters = message.toJSON()
retryIfNeeded(maxRetryCount) { invoke(Snode.Method.SendMessage, snode, destination, parameters)
invoke(Snode.Method.SendMessage, snode, destination, parameters)
}
}.toSet() }.toSet()
} }
} }