fix: Enable talkback actions in notifications and conversations (#661)
`ListStatusAccessiblityDelegate` was ignoring notifications because it was checking the status against the concrete `StatusViewData` and not the interface `IStatusViewData`. Fix that and now notifications have accessibility actions. `ConversationsFragment` didn't set the accessibility delegate, so no actions appeared. Fix that so they do.
This commit is contained in:
parent
b4780b146c
commit
f771dd7026
|
@ -55,6 +55,7 @@ import app.pachli.fragment.SFragment
|
|||
import app.pachli.interfaces.ActionButtonActivity
|
||||
import app.pachli.interfaces.ReselectableFragment
|
||||
import app.pachli.interfaces.StatusActionListener
|
||||
import app.pachli.util.ListStatusAccessibilityDelegate
|
||||
import app.pachli.util.StatusDisplayOptionsRepository
|
||||
import at.connyduck.sparkbutton.helpers.Utils
|
||||
import com.google.android.material.color.MaterialColors
|
||||
|
@ -220,6 +221,15 @@ class ConversationsFragment :
|
|||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
binding.recyclerView.setAccessibilityDelegateCompat(
|
||||
ListStatusAccessibilityDelegate(binding.recyclerView, this) { pos ->
|
||||
if (pos in 0 until adapter.itemCount) {
|
||||
adapter.peek(pos)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
)
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(context)
|
||||
|
||||
|
|
|
@ -26,8 +26,10 @@ import android.view.MenuInflater
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
|
@ -106,6 +108,8 @@ class NotificationsFragment :
|
|||
|
||||
private lateinit var layoutManager: LinearLayoutManager
|
||||
|
||||
private var talkBackWasEnabled = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
@ -149,7 +153,11 @@ class NotificationsFragment :
|
|||
binding.recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.setAccessibilityDelegateCompat(
|
||||
ListStatusAccessibilityDelegate(binding.recyclerView, this) { pos: Int ->
|
||||
adapter.snapshot().getOrNull(pos)
|
||||
if (pos in 0 until adapter.itemCount) {
|
||||
adapter.peek(pos)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
},
|
||||
)
|
||||
binding.recyclerView.addItemDecoration(
|
||||
|
@ -502,6 +510,14 @@ class NotificationsFragment :
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
val a11yManager = ContextCompat.getSystemService(requireContext(), AccessibilityManager::class.java)
|
||||
val wasEnabled = talkBackWasEnabled
|
||||
talkBackWasEnabled = a11yManager?.isEnabled == true
|
||||
if (talkBackWasEnabled && !wasEnabled) {
|
||||
adapter.notifyItemRangeChanged(0, adapter.itemCount)
|
||||
}
|
||||
|
||||
clearNotificationsForAccount(requireContext(), viewModel.account)
|
||||
}
|
||||
|
||||
|
|
|
@ -49,51 +49,50 @@ class ListStatusAccessibilityDelegate<T : IStatusViewData>(
|
|||
|
||||
val pos = recyclerView.getChildAdapterPosition(host)
|
||||
val status = statusProvider.getStatus(pos) ?: return
|
||||
if (status is StatusViewData) {
|
||||
if (status.spoilerText.isNotEmpty()) {
|
||||
info.addAction(if (status.isExpanded) collapseCwAction else expandCwAction)
|
||||
}
|
||||
|
||||
info.addAction(replyAction)
|
||||
|
||||
val actionable = status.actionable
|
||||
if (actionable.rebloggingAllowed()) {
|
||||
info.addAction(if (actionable.reblogged) unreblogAction else reblogAction)
|
||||
}
|
||||
info.addAction(if (actionable.favourited) unfavouriteAction else favouriteAction)
|
||||
info.addAction(if (actionable.bookmarked) unbookmarkAction else bookmarkAction)
|
||||
|
||||
val mediaActions = intArrayOf(
|
||||
R.id.action_open_media_1,
|
||||
R.id.action_open_media_2,
|
||||
R.id.action_open_media_3,
|
||||
R.id.action_open_media_4,
|
||||
)
|
||||
val attachmentCount = min(actionable.attachments.size, MAX_MEDIA_ATTACHMENTS)
|
||||
for (i in 0 until attachmentCount) {
|
||||
info.addAction(
|
||||
AccessibilityActionCompat(
|
||||
mediaActions[i],
|
||||
context.getString(R.string.action_open_media_n, i + 1),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
info.addAction(openProfileAction)
|
||||
if (getLinks(status).any()) info.addAction(linksAction)
|
||||
|
||||
val mentions = actionable.mentions
|
||||
if (mentions.isNotEmpty()) info.addAction(mentionsAction)
|
||||
|
||||
if (getHashtags(status).any()) info.addAction(hashtagsAction)
|
||||
if (!status.status.reblog?.account?.username.isNullOrEmpty()) {
|
||||
info.addAction(openRebloggerAction)
|
||||
}
|
||||
if (actionable.reblogsCount > 0) info.addAction(openRebloggedByAction)
|
||||
if (actionable.favouritesCount > 0) info.addAction(openFavsAction)
|
||||
|
||||
info.addAction(moreAction)
|
||||
if (status.spoilerText.isNotEmpty()) {
|
||||
info.addAction(if (status.isExpanded) collapseCwAction else expandCwAction)
|
||||
}
|
||||
|
||||
info.addAction(replyAction)
|
||||
|
||||
val actionable = status.actionable
|
||||
if (actionable.rebloggingAllowed()) {
|
||||
info.addAction(if (actionable.reblogged) unreblogAction else reblogAction)
|
||||
}
|
||||
info.addAction(if (actionable.favourited) unfavouriteAction else favouriteAction)
|
||||
info.addAction(if (actionable.bookmarked) unbookmarkAction else bookmarkAction)
|
||||
|
||||
val mediaActions = intArrayOf(
|
||||
R.id.action_open_media_1,
|
||||
R.id.action_open_media_2,
|
||||
R.id.action_open_media_3,
|
||||
R.id.action_open_media_4,
|
||||
)
|
||||
val attachmentCount = min(actionable.attachments.size, MAX_MEDIA_ATTACHMENTS)
|
||||
for (i in 0 until attachmentCount) {
|
||||
info.addAction(
|
||||
AccessibilityActionCompat(
|
||||
mediaActions[i],
|
||||
context.getString(R.string.action_open_media_n, i + 1),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
info.addAction(openProfileAction)
|
||||
if (getLinks(status).any()) info.addAction(linksAction)
|
||||
|
||||
val mentions = actionable.mentions
|
||||
if (mentions.isNotEmpty()) info.addAction(mentionsAction)
|
||||
|
||||
if (getHashtags(status).any()) info.addAction(hashtagsAction)
|
||||
if (!status.status.reblog?.account?.username.isNullOrEmpty()) {
|
||||
info.addAction(openRebloggerAction)
|
||||
}
|
||||
if (actionable.reblogsCount > 0) info.addAction(openRebloggedByAction)
|
||||
if (actionable.favouritesCount > 0) info.addAction(openFavsAction)
|
||||
|
||||
info.addAction(moreAction)
|
||||
}
|
||||
|
||||
override fun performAccessibilityAction(
|
||||
|
@ -230,7 +229,7 @@ class ListStatusAccessibilityDelegate<T : IStatusViewData>(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getLinks(status: StatusViewData): Sequence<LinkSpanInfo> {
|
||||
private fun getLinks(status: IStatusViewData): Sequence<LinkSpanInfo> {
|
||||
val content = status.content
|
||||
return if (content is Spannable) {
|
||||
content.getSpans(0, content.length, URLSpan::class.java)
|
||||
|
@ -248,7 +247,7 @@ class ListStatusAccessibilityDelegate<T : IStatusViewData>(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getHashtags(status: StatusViewData): Sequence<CharSequence> {
|
||||
private fun getHashtags(status: IStatusViewData): Sequence<CharSequence> {
|
||||
val content = status.content
|
||||
return content.getSpans(0, content.length, Object::class.java)
|
||||
.asSequence()
|
||||
|
|
Loading…
Reference in New Issue