Make messages with links selectable

This commit is contained in:
Philipp Heckel 2022-04-29 11:03:02 -04:00
parent 0cff98e72e
commit 4771ccc6c0
5 changed files with 51 additions and 9 deletions

View file

@ -188,7 +188,7 @@ class DetailActivity : AppCompatActivity(), ActionMode.Callback, NotificationFra
val onNotificationClick = { n: Notification -> onNotificationClick(n) }
val onNotificationLongClick = { n: Notification -> onNotificationLongClick(n) }
adapter = DetailAdapter(this, repository, onNotificationClick, onNotificationLongClick)
adapter = DetailAdapter(this, lifecycleScope, repository, onNotificationClick, onNotificationLongClick)
mainList = findViewById(R.id.detail_notification_list)
mainList.adapter = adapter

View file

@ -6,9 +6,11 @@ import android.content.*
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.RippleDrawable
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.Handler
import android.provider.MediaStore
import android.view.LayoutInflater
import android.view.View
@ -29,11 +31,10 @@ import io.heckel.ntfy.msg.DownloadWorker
import io.heckel.ntfy.msg.NotificationService
import io.heckel.ntfy.msg.NotificationService.Companion.ACTION_VIEW
import io.heckel.ntfy.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.*
class DetailAdapter(private val activity: Activity, private val repository: Repository, private val onClick: (Notification) -> Unit, private val onLongClick: (Notification) -> Unit) :
class DetailAdapter(private val activity: Activity, private val lifecycleScope: CoroutineScope, private val repository: Repository, private val onClick: (Notification) -> Unit, private val onLongClick: (Notification) -> Unit) :
ListAdapter<Notification, DetailAdapter.DetailViewHolder>(TopicDiffCallback) {
val selected = mutableSetOf<String>() // Notification IDs
@ -41,7 +42,7 @@ class DetailAdapter(private val activity: Activity, private val repository: Repo
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DetailViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_detail_item, parent, false)
return DetailViewHolder(activity, repository, view, selected, onClick, onLongClick)
return DetailViewHolder(activity, lifecycleScope, repository, view, selected, onClick, onLongClick)
}
/* Gets current topic and uses it to bind view. */
@ -62,9 +63,10 @@ class DetailAdapter(private val activity: Activity, private val repository: Repo
}
/* ViewHolder for Topic, takes in the inflated view and the onClick behavior. */
class DetailViewHolder(private val activity: Activity, private val repository: Repository, itemView: View, private val selected: Set<String>, val onClick: (Notification) -> Unit, val onLongClick: (Notification) -> Unit) :
class DetailViewHolder(private val activity: Activity, private val lifecycleScope: CoroutineScope, private val repository: Repository, itemView: View, private val selected: Set<String>, val onClick: (Notification) -> Unit, val onLongClick: (Notification) -> Unit) :
RecyclerView.ViewHolder(itemView) {
private var notification: Notification? = null
private val layout: View = itemView.findViewById(R.id.detail_item_layout)
private val cardView: CardView = itemView.findViewById(R.id.detail_item_card)
private val priorityImageView: ImageView = itemView.findViewById(R.id.detail_item_priority_image)
private val dateView: TextView = itemView.findViewById(R.id.detail_item_date_text)
@ -86,6 +88,17 @@ class DetailAdapter(private val activity: Activity, private val repository: Repo
dateView.text = formatDateShort(notification.timestamp)
messageView.text = maybeAppendActionErrors(formatMessage(notification), notification)
messageView.setOnClickListener {
// Click & Long-click listeners on the text as well, because "autoLink=web" makes them
// clickable, and so we cannot rely on the underlying card to perform the action.
// It's weird because "layout" is the ripple-able, but the card is clickable.
// See https://github.com/binwiederhier/ntfy/issues/226
layout.ripple(lifecycleScope)
onClick(notification)
}
messageView.setOnLongClickListener {
onLongClick(notification); true
}
newDotImageView.visibility = if (notification.notificationId == 0) View.GONE else View.VISIBLE
cardView.setOnClickListener { onClick(notification) }
cardView.setOnLongClickListener { onLongClick(notification); true }

View file

@ -9,6 +9,7 @@ import android.content.Context
import android.content.res.Configuration
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.content.res.Resources
import android.graphics.drawable.RippleDrawable
import android.net.Uri
import android.os.Build
import android.os.PowerManager
@ -25,6 +26,10 @@ import androidx.appcompat.app.AppCompatDelegate
import io.heckel.ntfy.R
import io.heckel.ntfy.db.*
import io.heckel.ntfy.msg.MESSAGE_ENCODING_BASE64
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody
@ -368,6 +373,29 @@ fun View.makeEndIconSmaller(resources: Resources) {
requestLayout()
}
// Shows the ripple effect on the view, if it is ripple-able, see https://stackoverflow.com/a/56314062/1440785
fun View.showRipple() {
if (background is RippleDrawable) {
background.state = intArrayOf(android.R.attr.state_pressed, android.R.attr.state_enabled)
}
}
// Hides the ripple effect on the view, if it is ripple-able, see https://stackoverflow.com/a/56314062/1440785
fun View.hideRipple() {
if (background is RippleDrawable) {
background.state = intArrayOf()
}
}
// Toggles the ripple effect on the view, if it is ripple-able
fun View.ripple(scope: CoroutineScope) {
showRipple()
scope.launch(Dispatchers.Main) {
delay(200)
hideRipple()
}
}
// TextWatcher that only implements the afterTextChanged method
class AfterChangedTextWatcher(val afterTextChangedFn: (s: Editable?) -> Unit) : TextWatcher {
override fun afterTextChanged(s: Editable?) {

View file

@ -20,6 +20,7 @@
app:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/detail_item_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"

View file

@ -3,8 +3,8 @@ Features:
Bugs:
* Accurate naming of "mute notifications" from "pause notifications" (#224, thanks to @shadow00 for reporting)
* Make messages with links selectable (#226, thanks to @StoyanDimitrov for reporting)
**Thanks for testing:**
Thanks to [@cmeis](https://github.com/cmeis), [@StoyanDimitrov](https://github.com/StoyanDimitrov), [@Fallenbagel](https://github.com/Fallenbagel) for testing, and
to [@Joeharrison94](https://github.com/Joeharrison94) for the input.
Thanks to @cmeis, @StoyanDimitrov, @Fallenbagel for testing, and to @Joeharrison94 for the input.