From 46444d141cb26e9bdd2ec89197436a6ea2a3ad38 Mon Sep 17 00:00:00 2001 From: puff <125275263+pufferss@users.noreply.github.com> Date: Thu, 6 Feb 2025 10:52:04 +0100 Subject: [PATCH 1/4] added copy links from message when long pressing message bubble --- .../org/fossify/messages/adapters/ThreadAdapter.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt b/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt index e1302894..dd600c79 100644 --- a/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt +++ b/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt @@ -29,6 +29,7 @@ import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.Target import org.fossify.commons.adapters.MyRecyclerViewListAdapter +import org.fossify.commons.compose.extensions.linkColor import org.fossify.commons.dialogs.ConfirmationDialog import org.fossify.commons.extensions.* import org.fossify.commons.helpers.SimpleContactsHelper @@ -49,6 +50,9 @@ import org.fossify.messages.models.Attachment import org.fossify.messages.models.Message import org.fossify.messages.models.ThreadItem import org.fossify.messages.models.ThreadItem.* +import kotlin.text.map +import kotlin.text.matches +import kotlin.text.toList class ThreadAdapter( activity: SimpleActivity, @@ -254,7 +258,6 @@ class ThreadAdapter( if (attachment != null) { putExtra(Intent.EXTRA_STREAM, attachment.getUri()) } - activity.startActivity(this) } } @@ -272,6 +275,11 @@ class ThreadAdapter( } } + private fun findLinks(text : String): List { + val regex = Regex("(https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]+\\.[^\\s]{2,})") + val links = regex.findAll(text) + return links.map { it.value }.toList() + } private fun setupView(holder: ViewHolder, view: View, message: Message) { ItemMessageBinding.bind(view).apply { threadMessageHolder.isSelected = selectedKeys.contains(message.hashCode()) @@ -281,6 +289,7 @@ class ThreadAdapter( beVisibleIf(message.body.isNotEmpty()) setOnLongClickListener { holder.viewLongClicked() + findLinks(text.toString()).map{ link -> activity.copyToClipboard(link)} true } From a9da954679d4c7370c505830790300af65aabe0b Mon Sep 17 00:00:00 2001 From: puff <125275263+pufferss@users.noreply.github.com> Date: Thu, 6 Feb 2025 11:29:33 +0100 Subject: [PATCH 2/4] fix format code --- .../fossify/messages/adapters/ThreadAdapter.kt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt b/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt index dd600c79..7417d0cf 100644 --- a/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt +++ b/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt @@ -29,7 +29,6 @@ import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.Target import org.fossify.commons.adapters.MyRecyclerViewListAdapter -import org.fossify.commons.compose.extensions.linkColor import org.fossify.commons.dialogs.ConfirmationDialog import org.fossify.commons.extensions.* import org.fossify.commons.helpers.SimpleContactsHelper @@ -50,9 +49,6 @@ import org.fossify.messages.models.Attachment import org.fossify.messages.models.Message import org.fossify.messages.models.ThreadItem import org.fossify.messages.models.ThreadItem.* -import kotlin.text.map -import kotlin.text.matches -import kotlin.text.toList class ThreadAdapter( activity: SimpleActivity, @@ -275,11 +271,16 @@ class ThreadAdapter( } } - private fun findLinks(text : String): List { - val regex = Regex("(https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]+\\.[^\\s]{2,})") + private fun findLinks(text: String): List { + val regex = + Regex( + "(https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]" + + "+[a-zA-Z0-9]\\.[^\\s]{2,}|https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]+\\.[^\\s]{2,})" + ) val links = regex.findAll(text) return links.map { it.value }.toList() } + private fun setupView(holder: ViewHolder, view: View, message: Message) { ItemMessageBinding.bind(view).apply { threadMessageHolder.isSelected = selectedKeys.contains(message.hashCode()) @@ -289,7 +290,7 @@ class ThreadAdapter( beVisibleIf(message.body.isNotEmpty()) setOnLongClickListener { holder.viewLongClicked() - findLinks(text.toString()).map{ link -> activity.copyToClipboard(link)} + findLinks(text.toString()).map { link -> activity.copyToClipboard(link) } true } From 2ea17d51bd27fa11b4e86d972fd27fe030780ede Mon Sep 17 00:00:00 2001 From: puff <125275263+pufferss@users.noreply.github.com> Date: Thu, 13 Feb 2025 10:45:26 +0100 Subject: [PATCH 3/4] implemented link copy when long pressing on it, by using linkmovementmethod subclass --- .../messages/adapters/ThreadAdapter.kt | 76 +++++++++++++++---- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt b/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt index 7417d0cf..2c4b105d 100644 --- a/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt +++ b/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt @@ -1,19 +1,25 @@ package org.fossify.messages.adapters import android.annotation.SuppressLint +import android.app.Activity import android.content.Intent import android.graphics.Color import android.graphics.Typeface import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable +import android.text.Spannable +import android.text.method.LinkMovementMethod +import android.text.style.URLSpan import android.util.Size import android.util.TypedValue import android.view.Menu +import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import android.widget.RelativeLayout +import android.widget.TextView import androidx.appcompat.content.res.AppCompatResources import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.updateLayoutParams @@ -50,6 +56,60 @@ import org.fossify.messages.models.Message import org.fossify.messages.models.ThreadItem import org.fossify.messages.models.ThreadItem.* +class CustomLinkMovementMethod(private val activity: Activity, private val holder: MyRecyclerViewListAdapter.ViewHolder) : LinkMovementMethod() { + + private var isLongPressDetected = false + private var startClickTime: Long = 0 + private val longPressTime = 180L + + override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean { + when (event.action) { + MotionEvent.ACTION_DOWN -> { + startClickTime = System.currentTimeMillis() + isLongPressDetected = false + } + + MotionEvent.ACTION_MOVE -> { + if (!isLongPressDetected) { + val elapsedTime = System.currentTimeMillis() - startClickTime + if (elapsedTime >= longPressTime) { + isLongPressDetected = true + holder.viewLongClicked() + val x = event.x.toInt() + val y = event.y.toInt() + val link = getLinkAt(widget, buffer, x, y) + + link?.let { + activity.copyToClipboard(it) + return true + } + } + } + } + + MotionEvent.ACTION_UP -> { + if (isLongPressDetected) { + return true + } + } + + MotionEvent.ACTION_CANCEL -> { + isLongPressDetected = false + } + } + + return super.onTouchEvent(widget, buffer, event) + } + + private fun getLinkAt(widget: TextView, buffer: Spannable, x: Int, y: Int): String? { + val layout = widget.layout + val line = layout.getLineForVertical(y) + val offset = layout.getOffsetForHorizontal(line, x.toFloat()) + val urlSpan = buffer.getSpans(offset, offset, URLSpan::class.java).firstOrNull() + return urlSpan?.url + } +} + class ThreadAdapter( activity: SimpleActivity, recyclerView: MyRecyclerView, @@ -271,16 +331,6 @@ class ThreadAdapter( } } - private fun findLinks(text: String): List { - val regex = - Regex( - "(https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]" + - "+[a-zA-Z0-9]\\.[^\\s]{2,}|https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]+\\.[^\\s]{2,})" - ) - val links = regex.findAll(text) - return links.map { it.value }.toList() - } - private fun setupView(holder: ViewHolder, view: View, message: Message) { ItemMessageBinding.bind(view).apply { threadMessageHolder.isSelected = selectedKeys.contains(message.hashCode()) @@ -288,11 +338,7 @@ class ThreadAdapter( text = message.body setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize) beVisibleIf(message.body.isNotEmpty()) - setOnLongClickListener { - holder.viewLongClicked() - findLinks(text.toString()).map { link -> activity.copyToClipboard(link) } - true - } + threadMessageBody.movementMethod = CustomLinkMovementMethod(activity, holder) setOnClickListener { holder.viewClicked(message) From e179e782bdb5fd4cdf7649c6d9135fc7306d4b93 Mon Sep 17 00:00:00 2001 From: puff <125275263+pufferss@users.noreply.github.com> Date: Thu, 13 Feb 2025 10:56:41 +0100 Subject: [PATCH 4/4] fix code format --- .../main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt b/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt index 2c4b105d..4b622867 100644 --- a/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt +++ b/app/src/main/kotlin/org/fossify/messages/adapters/ThreadAdapter.kt @@ -314,6 +314,7 @@ class ThreadAdapter( if (attachment != null) { putExtra(Intent.EXTRA_STREAM, attachment.getUri()) } + activity.startActivity(this) } }