diff --git a/app/src/main/java/org/mozilla/fenix/utils/Undo.kt b/app/src/main/java/org/mozilla/fenix/utils/Undo.kt
index 5d5d109a0..f9512be2c 100644
--- a/app/src/main/java/org/mozilla/fenix/utils/Undo.kt
+++ b/app/src/main/java/org/mozilla/fenix/utils/Undo.kt
@@ -10,6 +10,10 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.mozilla.fenix.components.FenixSnackbar
+import android.app.AlertDialog
+import org.mozilla.fenix.R
+import android.content.Context
+import android.view.accessibility.AccessibilityManager
import java.util.concurrent.atomic.AtomicBoolean
internal const val UNDO_DELAY = 3000L
@@ -40,29 +44,58 @@ fun CoroutineScope.allowUndo(
// writing a volatile variable.
val requestedUndo = AtomicBoolean(false)
- // Launch an indefinite snackbar.
- val snackbar = FenixSnackbar
- .make(view, FenixSnackbar.LENGTH_INDEFINITE)
- .setText(message)
- .setAnchorView(anchorView)
- .setAction(undoActionTitle) {
- requestedUndo.set(true)
+ fun showUndoDialog() {
+ val dialogBuilder = AlertDialog.Builder(view.context)
+ dialogBuilder.setMessage(message).setCancelable(false)
+ .setPositiveButton(R.string.a11y_dialog_deleted_confirm) { _, _ ->
+ launch {
+ operation.invoke()
+ }
+ }.setNegativeButton(R.string.a11y_dialog_deleted_undo) { _, _ ->
launch {
onCancel.invoke()
}
}
+ val alert = dialogBuilder.create()
+ alert.show()
+ }
- // If user engages with the snackbar, it'll get automatically dismissed.
- snackbar.show()
+ fun showUndoSnackbar() {
+ val snackbar = FenixSnackbar
+ .make(view, FenixSnackbar.LENGTH_INDEFINITE)
+ .setText(message)
+ .setAnchorView(anchorView)
+ .setAction(undoActionTitle) {
+ requestedUndo.set(true)
+ launch {
+ onCancel.invoke()
+ }
+ }
- // Wait a bit, and if user didn't request cancellation, proceed with
- // requested operation and hide the snackbar.
- launch {
- delay(UNDO_DELAY)
+ snackbar.show()
- if (!requestedUndo.get()) {
- snackbar.dismiss()
- operation.invoke()
+ // Wait a bit, and if user didn't request cancellation, proceed with
+ // requested operation and hide the snackbar.
+ launch {
+ delay(UNDO_DELAY)
+
+ if (!requestedUndo.get()) {
+ snackbar.dismiss()
+ operation.invoke()
+ }
}
}
+
+ // It is difficult to use our Snackbars quickly enough with
+ // Talkback enabled, so in that case we show a dialog instead
+ if (touchExplorationEnabled(view)) {
+ showUndoDialog()
+ } else {
+ showUndoSnackbar()
+ }
+}
+
+fun touchExplorationEnabled(view: View): Boolean {
+ val am = view.context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
+ return am.isTouchExplorationEnabled
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 45f4633d5..aae590cad 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -653,6 +653,10 @@
Private tabs deleted
UNDO
+
+ Undo
+
+ Confirm
Allow %1$s to open %2$s