mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
Merge pull request #729 from hjubb/copy_url_link
Allow copying URL from conversation
This commit is contained in:
commit
4533a25a3c
@ -1338,12 +1338,21 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
|
|
||||||
override fun copyMessages(messages: Set<MessageRecord>) {
|
override fun copyMessages(messages: Set<MessageRecord>) {
|
||||||
val sortedMessages = messages.sortedBy { it.dateSent }
|
val sortedMessages = messages.sortedBy { it.dateSent }
|
||||||
|
val messageSize = sortedMessages.size
|
||||||
val builder = StringBuilder()
|
val builder = StringBuilder()
|
||||||
for (message in sortedMessages) {
|
val messageIterator = sortedMessages.iterator()
|
||||||
|
while (messageIterator.hasNext()) {
|
||||||
|
val message = messageIterator.next()
|
||||||
val body = MentionUtilities.highlightMentions(message.body, threadID, this)
|
val body = MentionUtilities.highlightMentions(message.body, threadID, this)
|
||||||
if (TextUtils.isEmpty(body)) { continue }
|
if (TextUtils.isEmpty(body)) { continue }
|
||||||
|
if (messageSize > 1) {
|
||||||
val formattedTimestamp = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), message.timestamp)
|
val formattedTimestamp = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), message.timestamp)
|
||||||
builder.append("$formattedTimestamp: $body").append('\n')
|
builder.append("$formattedTimestamp: ")
|
||||||
|
}
|
||||||
|
builder.append(body)
|
||||||
|
if (messageIterator.hasNext()) {
|
||||||
|
builder.append('\n')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (builder.isNotEmpty() && builder[builder.length - 1] == '\n') {
|
if (builder.isNotEmpty() && builder[builder.length - 1] == '\n') {
|
||||||
builder.deleteCharAt(builder.length - 1)
|
builder.deleteCharAt(builder.length - 1)
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
package org.thoughtcrime.securesms.conversation.v2
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context.CLIPBOARD_SERVICE
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Spannable
|
||||||
|
import android.text.SpannableStringBuilder
|
||||||
|
import android.text.style.StyleSpan
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
|
import kotlinx.android.synthetic.main.fragment_modal_url_bottom_sheet.*
|
||||||
|
import network.loki.messenger.R
|
||||||
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
|
|
||||||
|
class ModalUrlBottomSheet(private val url: String): BottomSheetDialogFragment(), View.OnClickListener {
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_modal_url_bottom_sheet, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
val explanation = resources.getString(R.string.dialog_open_url_explanation, url)
|
||||||
|
val spannable = SpannableStringBuilder(explanation)
|
||||||
|
val startIndex = explanation.indexOf(url)
|
||||||
|
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + url.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
openURLExplanationTextView.text = spannable
|
||||||
|
cancelButton.setOnClickListener(this)
|
||||||
|
copyButton.setOnClickListener(this)
|
||||||
|
openURLButton.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun open() {
|
||||||
|
try {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||||
|
requireContext().startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Toast.makeText(context, R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copy() {
|
||||||
|
val clip = ClipData.newPlainText("URL", url)
|
||||||
|
val manager = requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
manager.setPrimaryClip(clip)
|
||||||
|
Toast.makeText(requireContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
val window = dialog?.window ?: return
|
||||||
|
val isLightMode = UiModeUtilities.isDayUiMode(requireContext())
|
||||||
|
window.setDimAmount(if (isLightMode) 0.1f else 0.75f)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View?) {
|
||||||
|
when (v) {
|
||||||
|
openURLButton -> open()
|
||||||
|
copyButton -> copy()
|
||||||
|
cancelButton -> dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,40 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.v2.dialogs
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.net.Uri
|
|
||||||
import android.text.Spannable
|
|
||||||
import android.text.SpannableStringBuilder
|
|
||||||
import android.text.style.StyleSpan
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import kotlinx.android.synthetic.main.dialog_open_url.view.*
|
|
||||||
import network.loki.messenger.R
|
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
|
|
||||||
|
|
||||||
/** Shown upon tapping a URL. */
|
|
||||||
class OpenURLDialog(private val url: String) : BaseDialog() {
|
|
||||||
|
|
||||||
override fun setContentView(builder: AlertDialog.Builder) {
|
|
||||||
val contentView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_open_url, null)
|
|
||||||
val explanation = resources.getString(R.string.dialog_open_url_explanation, url)
|
|
||||||
val spannable = SpannableStringBuilder(explanation)
|
|
||||||
val startIndex = explanation.indexOf(url)
|
|
||||||
spannable.setSpan(StyleSpan(Typeface.BOLD), startIndex, startIndex + url.count(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
||||||
contentView.openURLExplanationTextView.text = spannable
|
|
||||||
contentView.cancelButton.setOnClickListener { dismiss() }
|
|
||||||
contentView.openURLButton.setOnClickListener { open() }
|
|
||||||
builder.setView(contentView)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun open() {
|
|
||||||
try {
|
|
||||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
||||||
requireContext().startActivity(intent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Toast.makeText(context, R.string.invalid_url, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.conversation.v2.messages
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.text.method.LinkMovementMethod
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
@ -11,20 +10,17 @@ import android.widget.LinearLayout
|
|||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
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.text.getSpans
|
|
||||||
import androidx.core.text.toSpannable
|
|
||||||
import androidx.core.view.isVisible
|
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.ModalUrlBottomSheet
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MessageBubbleUtilities
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
||||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
import org.thoughtcrime.securesms.mms.GlideRequests
|
||||||
import org.thoughtcrime.securesms.mms.ImageSlide
|
import org.thoughtcrime.securesms.mms.ImageSlide
|
||||||
|
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||||
|
|
||||||
class LinkPreviewView : LinearLayout {
|
class LinkPreviewView : LinearLayout {
|
||||||
private val cornerMask by lazy { CornerMask(this) }
|
private val cornerMask by lazy { CornerMask(this) }
|
||||||
@ -97,7 +93,7 @@ class LinkPreviewView : LinearLayout {
|
|||||||
fun openURL() {
|
fun openURL() {
|
||||||
val url = this.url ?: return
|
val url = this.url ?: return
|
||||||
val activity = context as AppCompatActivity
|
val activity = context as AppCompatActivity
|
||||||
OpenURLDialog(url).show(activity.supportFragmentManager, "Open URL Dialog")
|
ModalUrlBottomSheet(url).show(activity.supportFragmentManager, "Open URL Dialog")
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
@ -31,8 +31,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.components.emoji.EmojiTextView
|
import org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||||
|
import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet
|
||||||
import org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView
|
import org.thoughtcrime.securesms.conversation.v2.components.AlbumThumbnailView
|
||||||
import org.thoughtcrime.securesms.conversation.v2.dialogs.OpenURLDialog
|
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan
|
||||||
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
import org.thoughtcrime.securesms.conversation.v2.utilities.TextUtilities.getIntersectedModalSpans
|
||||||
@ -237,7 +237,7 @@ class VisibleMessageContentView : LinearLayout {
|
|||||||
val updatedUrl = urlSpan.url.let { HttpUrl.parse(it).toString() }
|
val updatedUrl = urlSpan.url.let { HttpUrl.parse(it).toString() }
|
||||||
val replacementSpan = ModalURLSpan(updatedUrl) { url ->
|
val replacementSpan = ModalURLSpan(updatedUrl) { url ->
|
||||||
val activity = context as AppCompatActivity
|
val activity = context as AppCompatActivity
|
||||||
OpenURLDialog(url).show(activity.supportFragmentManager, "Open URL Dialog")
|
ModalUrlBottomSheet(url).show(activity.supportFragmentManager, "Open URL Dialog")
|
||||||
}
|
}
|
||||||
val start = body.getSpanStart(urlSpan)
|
val start = body.getSpanStart(urlSpan)
|
||||||
val end = body.getSpanEnd(urlSpan)
|
val end = body.getSpanEnd(urlSpan)
|
||||||
|
57
app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml
Normal file
57
app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?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:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:behavior_hideable="true"
|
||||||
|
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:paddingTop="@dimen/large_spacing">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/openURLTitleTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/dialog_open_url_title"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textSize="@dimen/large_font_size" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/openURLExplanationTextView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/dialog_open_url_explanation"
|
||||||
|
android:paddingHorizontal="@dimen/medium_spacing"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
android:textSize="@dimen/small_font_size"
|
||||||
|
android:layout_margin="@dimen/large_spacing"
|
||||||
|
android:textAlignment="center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/openURLButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/BottomSheetActionItem"
|
||||||
|
android:text="@string/open"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/copyButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/BottomSheetActionItem"
|
||||||
|
android:text="@string/copy_url"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:textColor="@color/destructive"
|
||||||
|
android:id="@+id/cancelButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/BottomSheetActionItem"
|
||||||
|
android:text="@string/cancel"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -872,6 +872,7 @@
|
|||||||
<string name="dialog_open_url_title">Open URL?</string>
|
<string name="dialog_open_url_title">Open URL?</string>
|
||||||
<string name="dialog_open_url_explanation">Are you sure you want to open %s?</string>
|
<string name="dialog_open_url_explanation">Are you sure you want to open %s?</string>
|
||||||
<string name="open">Open</string>
|
<string name="open">Open</string>
|
||||||
|
<string name="copy_url">Copy URL</string>
|
||||||
|
|
||||||
<string name="dialog_link_preview_title">Enable Link Previews?</string>
|
<string name="dialog_link_preview_title">Enable Link Previews?</string>
|
||||||
<string name="dialog_link_preview_explanation">Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.</string>
|
<string name="dialog_link_preview_explanation">Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session\'s settings.</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user