From 66e95787a27a6dcdc961b0163ef4223e135dae98 Mon Sep 17 00:00:00 2001 From: Harris Date: Tue, 14 Sep 2021 10:27:34 +1000 Subject: [PATCH 1/4] feat: add bottom sheet modal url with copy option --- .../conversation/v2/ModalUrlBottomSheet.kt | 72 +++++++++++++++++++ .../conversation/v2/dialogs/OpenURLDialog.kt | 40 ----------- .../v2/messages/LinkPreviewView.kt | 10 +-- .../v2/messages/VisibleMessageContentView.kt | 4 +- .../fragment_modal_url_bottom_sheet.xml | 58 +++++++++++++++ 5 files changed, 135 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ModalUrlBottomSheet.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/OpenURLDialog.kt create mode 100644 app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ModalUrlBottomSheet.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ModalUrlBottomSheet.kt new file mode 100644 index 0000000000..859f208a52 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ModalUrlBottomSheet.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/OpenURLDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/OpenURLDialog.kt deleted file mode 100644 index ea0230f578..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/dialogs/OpenURLDialog.kt +++ /dev/null @@ -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() - } -} \ No newline at end of file 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 0457f82702..45921122f2 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 @@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.conversation.v2.messages import android.content.Context import android.graphics.Canvas import android.graphics.Rect -import android.text.method.LinkMovementMethod import android.util.AttributeSet import android.view.LayoutInflater import android.view.MotionEvent @@ -11,20 +10,17 @@ import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat -import androidx.core.text.getSpans -import androidx.core.text.toSpannable 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.ModalUrlBottomSheet 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.database.model.MmsMessageRecord -import org.thoughtcrime.securesms.util.UiModeUtilities import org.thoughtcrime.securesms.mms.GlideRequests import org.thoughtcrime.securesms.mms.ImageSlide +import org.thoughtcrime.securesms.util.UiModeUtilities class LinkPreviewView : LinearLayout { private val cornerMask by lazy { CornerMask(this) } @@ -97,7 +93,7 @@ class LinkPreviewView : LinearLayout { fun openURL() { val url = this.url ?: return val activity = context as AppCompatActivity - OpenURLDialog(url).show(activity.supportFragmentManager, "Open URL Dialog") + ModalUrlBottomSheet(url).show(activity.supportFragmentManager, "Open URL Dialog") } // endregion } \ No newline at end of file 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 38831ed5ab..f98b3a23dd 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 @@ -31,8 +31,8 @@ import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.components.emoji.EmojiTextView 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.dialogs.OpenURLDialog import org.thoughtcrime.securesms.conversation.v2.utilities.MentionUtilities import org.thoughtcrime.securesms.conversation.v2.utilities.ModalURLSpan 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 replacementSpan = ModalURLSpan(updatedUrl) { url -> 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 end = body.getSpanEnd(urlSpan) diff --git a/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml b/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml new file mode 100644 index 0000000000..dd5907d679 --- /dev/null +++ b/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + \ No newline at end of file From 790436bad85a9d5d74da42d45672f85f313e1f51 Mon Sep 17 00:00:00 2001 From: Harris Date: Tue, 14 Sep 2021 14:14:42 +1000 Subject: [PATCH 2/4] refactor: change the "copy" text to "copy URL" for added context --- app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml | 5 ++--- app/src/main/res/values/strings.xml | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml b/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml index dd5907d679..8fb7c17f87 100644 --- a/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml +++ b/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml @@ -7,8 +7,7 @@ app:behavior_hideable="true" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior" android:gravity="center_horizontal" - android:paddingVertical="@dimen/large_spacing" - > + android:paddingVertical="@dimen/large_spacing"> Open URL? Are you sure you want to open %s? Open + Copy URL Enable Link Previews? 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. From 792dc2752c29659da99388e9fc6b5d666b05d0aa Mon Sep 17 00:00:00 2001 From: Harris Date: Tue, 14 Sep 2021 14:15:40 +1000 Subject: [PATCH 3/4] fix: remove bottom padding in layout --- app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml b/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml index 8fb7c17f87..190d4ef0c7 100644 --- a/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml +++ b/app/src/main/res/layout/fragment_modal_url_bottom_sheet.xml @@ -7,7 +7,7 @@ app:behavior_hideable="true" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior" android:gravity="center_horizontal" - android:paddingVertical="@dimen/large_spacing"> + android:paddingTop="@dimen/large_spacing"> Date: Wed, 15 Sep 2021 11:04:43 +1000 Subject: [PATCH 4/4] fix: copy message fixed for single messages --- .../conversation/v2/ConversationActivityV2.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) 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 c439862bac..e0453e10c7 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 @@ -1338,12 +1338,21 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe override fun copyMessages(messages: Set) { val sortedMessages = messages.sortedBy { it.dateSent } + val messageSize = sortedMessages.size 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) if (TextUtils.isEmpty(body)) { continue } - val formattedTimestamp = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), message.timestamp) - builder.append("$formattedTimestamp: $body").append('\n') + if (messageSize > 1) { + val formattedTimestamp = DateUtils.getDisplayFormattedTimeSpanString(this, Locale.getDefault(), message.timestamp) + builder.append("$formattedTimestamp: ") + } + builder.append(body) + if (messageIterator.hasNext()) { + builder.append('\n') + } } if (builder.isNotEmpty() && builder[builder.length - 1] == '\n') { builder.deleteCharAt(builder.length - 1)