From f2ed6a0dab3181a90307161324afe8e55d0d48bb Mon Sep 17 00:00:00 2001 From: Nik Clayton Date: Tue, 19 Nov 2024 15:04:22 +0100 Subject: [PATCH] fix: Ensure items in accessibility dialogs are clickable (#1112) The copy button meant that some dialogs did not return the item click. Fix this by having the adapter listen for clicks and forward them on. Pre-emptively move the adapter to core.ui, as it's going to be useful for the other accessiblity delegates. Fixes #1108 --- .../util/ListStatusAccessibilityDelegate.kt | 70 ++++------------ app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values-fi/strings.xml | 2 - app/src/main/res/values-ga/strings.xml | 2 - app/src/main/res/values/strings.xml | 2 - .../core/ui/ArrayAdapterWithCopyButton.kt | 82 +++++++++++++++++++ .../main/res/drawable/ic_content_copy_24.xml | 0 .../layout/simple_list_item_1_copy_button.xml | 2 +- core/ui/src/main/res/values-es/strings.xml | 2 + core/ui/src/main/res/values-fi/strings.xml | 2 + core/ui/src/main/res/values-ga/strings.xml | 4 +- core/ui/src/main/res/values/strings.xml | 2 + 12 files changed, 107 insertions(+), 67 deletions(-) create mode 100644 core/ui/src/main/kotlin/app/pachli/core/ui/ArrayAdapterWithCopyButton.kt rename {feature/about => core/ui}/src/main/res/drawable/ic_content_copy_24.xml (100%) rename {app => core/ui}/src/main/res/layout/simple_list_item_1_copy_button.xml (98%) diff --git a/app/src/main/java/app/pachli/util/ListStatusAccessibilityDelegate.kt b/app/src/main/java/app/pachli/util/ListStatusAccessibilityDelegate.kt index 0ec9aa6b6..b6a8ffb39 100644 --- a/app/src/main/java/app/pachli/util/ListStatusAccessibilityDelegate.kt +++ b/app/src/main/java/app/pachli/util/ListStatusAccessibilityDelegate.kt @@ -1,21 +1,13 @@ package app.pachli.util -import android.content.ClipData -import android.content.ClipboardManager import android.content.Context -import android.os.Build import android.os.Bundle import android.text.Spannable import android.text.style.URLSpan -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityManager -import android.widget.ArrayAdapter -import android.widget.Toast import androidx.appcompat.app.AlertDialog -import androidx.core.content.ContextCompat.getSystemService import androidx.core.view.AccessibilityDelegateCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat @@ -26,7 +18,7 @@ import app.pachli.adapter.FilterableStatusViewHolder import app.pachli.adapter.StatusBaseViewHolder import app.pachli.core.activity.openLink import app.pachli.core.network.model.Status.Companion.MAX_MEDIA_ATTACHMENTS -import app.pachli.databinding.SimpleListItem1CopyButtonBinding +import app.pachli.core.ui.ArrayAdapterWithCopyButton import app.pachli.interfaces.StatusActionListener import app.pachli.viewdata.IStatusViewData import app.pachli.viewdata.NotificationViewData @@ -222,8 +214,9 @@ class ListStatusAccessibilityDelegate( ArrayAdapterWithCopyButton( host.context, textLinks, - ), - ) { _, which -> host.context.openLink(links[which].link) } + ) { position -> host.context.openLink(links[position].link) }, + null, + ) .show() .let { forceFocus(it.listView) } } @@ -241,10 +234,11 @@ class ListStatusAccessibilityDelegate( ArrayAdapterWithCopyButton( host.context, stringMentions, - ), - ) { _, which -> - statusActionListener.onViewAccount(mentions[which].id) - } + ) { position -> + statusActionListener.onViewAccount(mentions[position].id) + }, + null, + ) .show() .let { forceFocus(it.listView) } } @@ -258,10 +252,11 @@ class ListStatusAccessibilityDelegate( ArrayAdapterWithCopyButton( host.context, tags, - ), - ) { _, which -> - statusActionListener.onViewTag(tags[which].toString()) - } + ) { position -> + statusActionListener.onViewTag(tags[position].toString()) + }, + null, + ) .show() .let { forceFocus(it.listView) } } @@ -413,40 +408,3 @@ class ListStatusAccessibilityDelegate( private data class LinkSpanInfo(val text: String, val link: String) } - -/** - * An [ArrayAdapter] that shows a "copy" button next to each item. When clicked - * the text of the item is copied to the clipboard and a toast is shown (if - * appropriate). - */ -private class ArrayAdapterWithCopyButton( - context: Context, - items: List, -) : ArrayAdapter(context, R.layout.simple_list_item_1_copy_button, items) { - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val binding = if (convertView == null) { - SimpleListItem1CopyButtonBinding.inflate(LayoutInflater.from(context), parent, false) - } else { - SimpleListItem1CopyButtonBinding.bind(convertView) - } - - getItem(position)?.let { text -> - binding.text1.text = text - - binding.copy.setOnClickListener { - val clipboard = getSystemService(context, ClipboardManager::class.java) as ClipboardManager - val clip = ClipData.newPlainText("", text) - clipboard.setPrimaryClip(clip) - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { - Toast.makeText( - context, - context.getString(R.string.item_copied), - Toast.LENGTH_SHORT, - ).show() - } - } - } - - return binding.root - } -} diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f9cabb838..ee697181c 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -782,8 +782,6 @@ Revisar idioma de la publicación antes de publicar Publicar como está (%1$s) y no volver a preguntar Se intentará subir nuevamente cuando envíes la publicación. Si vuelve a fallar, la publicación se guardará en tus borradores.\n\nEl error fue: %1$s - Texto copiado - Copiar ítem %1$s seguidor eliminado %1$s seguidores eliminados @@ -798,4 +796,4 @@ El inicio de sesión falló con el siguiente error:\n\n%1$s La actualización de la cuenta falló con el siguiente error:\n\n%1$s\n\nPuedes continuar, pero tus listas y filtros pueden estar incompletos. Volver a iniciar sesión - \ No newline at end of file + diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 4232583db..3505de862 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -765,6 +765,4 @@ Ei muita osanottajia Julkaise muutoksetta (%1$s) äläkä kysy uudelleen Tarkasta julkaisun kieli ennen julkaisemista - Kopioi kohde - Teksti kopioitu diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 86d2e37fe..998241302 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -527,8 +527,6 @@ Gach post ▾ Do leabharlann Poist i do leabharlann agus i do phoist phoiblí - Cóipeáil mír - Cóipeáladh téacs Déanfar an t-uaslódáil a aisghabháil nuair a sheolann tú an post. Má theipeann air arís sábhálfar an post i do dhréachtaí.\n\nEarráid a bhí ann: %1$s Mionathraigh iatán laethanta 30 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d7926c4d9..71f93f3c2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -850,8 +850,6 @@ Public posts Public, searchable posts known by server - Copy item - Text copied The upload will be retried when you send the post. If it fails again the post will be saved in your drafts.\n\nThe error was: %1$s Modify attachment diff --git a/core/ui/src/main/kotlin/app/pachli/core/ui/ArrayAdapterWithCopyButton.kt b/core/ui/src/main/kotlin/app/pachli/core/ui/ArrayAdapterWithCopyButton.kt new file mode 100644 index 000000000..ae113b788 --- /dev/null +++ b/core/ui/src/main/kotlin/app/pachli/core/ui/ArrayAdapterWithCopyButton.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2024 Pachli Association + * + * This file is a part of Pachli. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Pachli; if not, + * see . + */ + +package app.pachli.core.ui + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.os.Build +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.Toast +import androidx.core.content.ContextCompat +import app.pachli.core.ui.databinding.SimpleListItem1CopyButtonBinding + +/** + * An [ArrayAdapter] that shows a "copy" button next to each item. + * + * If the "copy" button is clicked the text of the item is copied to the clipboard + * and a toast is shown (if appropriate). + * + * If the item is clicked then [listener] is called with the position of the + * clicked item. + */ +class ArrayAdapterWithCopyButton( + context: Context, + items: List, + private val listener: OnClickListener, +) : ArrayAdapter(context, R.layout.simple_list_item_1_copy_button, android.R.id.text1, items) { + fun interface OnClickListener { + /** @param position Index of the item the user clicked. */ + fun onClick(position: Int) + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val binding = if (convertView == null) { + SimpleListItem1CopyButtonBinding.inflate(LayoutInflater.from(context), parent, false).apply { + text1.setOnClickListener { listener.onClick(position) } + + copy.setOnClickListener { + getItem(position)?.let { text -> + val clipboard = ContextCompat.getSystemService( + context, + ClipboardManager::class.java, + ) as ClipboardManager + val clip = ClipData.newPlainText("", text) + clipboard.setPrimaryClip(clip) + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) { + Toast.makeText( + context, + context.getString(R.string.item_copied), + Toast.LENGTH_SHORT, + ).show() + } + } + } + } + } else { + SimpleListItem1CopyButtonBinding.bind(convertView) + } + + getItem(position)?.let { binding.text1.text = it } + + return binding.root + } +} diff --git a/feature/about/src/main/res/drawable/ic_content_copy_24.xml b/core/ui/src/main/res/drawable/ic_content_copy_24.xml similarity index 100% rename from feature/about/src/main/res/drawable/ic_content_copy_24.xml rename to core/ui/src/main/res/drawable/ic_content_copy_24.xml diff --git a/app/src/main/res/layout/simple_list_item_1_copy_button.xml b/core/ui/src/main/res/layout/simple_list_item_1_copy_button.xml similarity index 98% rename from app/src/main/res/layout/simple_list_item_1_copy_button.xml rename to core/ui/src/main/res/layout/simple_list_item_1_copy_button.xml index 9a3ce24ef..2bbb73110 100644 --- a/app/src/main/res/layout/simple_list_item_1_copy_button.xml +++ b/core/ui/src/main/res/layout/simple_list_item_1_copy_button.xml @@ -31,10 +31,10 @@ android:id="@android:id/text1" android:layout_width="0dp" android:layout_height="match_parent" - android:layout_gravity="center" android:layout_weight="1" android:gravity="center_vertical" android:textAppearance="?android:attr/textAppearanceListItemSmall" + android:focusable="true" tools:ignore="SelectableText" tools:text="#TestHashTag" /> diff --git a/core/ui/src/main/res/values-es/strings.xml b/core/ui/src/main/res/values-es/strings.xml index e433d0bea..5f6840edd 100644 --- a/core/ui/src/main/res/values-es/strings.xml +++ b/core/ui/src/main/res/values-es/strings.xml @@ -14,4 +14,6 @@ Etiquetas Enlaces Etiquetas + Texto copiado + Copiar ítem diff --git a/core/ui/src/main/res/values-fi/strings.xml b/core/ui/src/main/res/values-fi/strings.xml index c5d43f7c1..c641d4537 100644 --- a/core/ui/src/main/res/values-fi/strings.xml +++ b/core/ui/src/main/res/values-fi/strings.xml @@ -14,4 +14,6 @@ Aihetunnisteet Linkit Aihetunnisteet + Teksti kopioitu + Kopioi kohde diff --git a/core/ui/src/main/res/values-ga/strings.xml b/core/ui/src/main/res/values-ga/strings.xml index 7e08e6344..bd5213b74 100644 --- a/core/ui/src/main/res/values-ga/strings.xml +++ b/core/ui/src/main/res/values-ga/strings.xml @@ -14,4 +14,6 @@ Haischlibeanna Athnuaigh " (🔗 %s)" - \ No newline at end of file + Cóipeáladh téacs + Cóipeáil mír + diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index f1359eb51..b6de9af80 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -14,4 +14,6 @@ Hashtags Links Hashtags + Text copied + Copy item