Make messages with links selectable
This commit is contained in:
parent
0cff98e72e
commit
4771ccc6c0
5 changed files with 51 additions and 9 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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?) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue