Skip to content

Commit b2b06fa

Browse files
authored
feat: FAB and menu items accessible / usable with keyboard
1 parent 2ca893e commit b2b06fa

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

AnkiDroid/src/main/java/com/ichi2/anki/DeckPickerFloatingActionMenu.kt

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import android.animation.Animator
1919
import android.content.Context
2020
import android.content.res.ColorStateList
2121
import android.provider.Settings
22+
import android.view.KeyEvent
2223
import android.view.MotionEvent
2324
import android.view.View
2425
import android.widget.LinearLayout
@@ -355,6 +356,21 @@ class DeckPickerFloatingActionMenu(
355356
return animDuration != 0f && animTransition != 0f && animWindow != 0f
356357
}
357358

359+
private fun createActivationKeyListener(
360+
logMessage: String,
361+
action: () -> Unit,
362+
): View.OnKeyListener =
363+
View.OnKeyListener { _, keyCode, event ->
364+
if (event.action == KeyEvent.ACTION_DOWN &&
365+
(keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_CENTER)
366+
) {
367+
Timber.d(logMessage)
368+
action()
369+
return@OnKeyListener true
370+
}
371+
false
372+
}
373+
358374
init {
359375
val addSharedButton: FloatingActionButton = view.findViewById(R.id.add_shared_action)
360376
val addDeckButton: FloatingActionButton = view.findViewById(R.id.add_deck_action)
@@ -380,6 +396,32 @@ class DeckPickerFloatingActionMenu(
380396
}
381397
},
382398
)
399+
400+
// Enable keyboard activation for Enter/DPAD_CENTER/ESC keys
401+
fabMain.setOnKeyListener { _, keyCode, event ->
402+
if (event.action == KeyEvent.ACTION_DOWN) {
403+
when (keyCode) {
404+
KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_DPAD_CENTER -> {
405+
Timber.d("FAB main button: ENTER key pressed")
406+
if (!isFABOpen) {
407+
showFloatingActionMenu()
408+
} else {
409+
addNote()
410+
}
411+
return@setOnKeyListener true
412+
}
413+
KeyEvent.KEYCODE_ESCAPE -> {
414+
if (isFABOpen) {
415+
Timber.d("FAB main button: ESC key pressed - closing menu")
416+
closeFloatingActionMenu(applyRiseAndShrinkAnimation = true)
417+
return@setOnKeyListener true
418+
}
419+
}
420+
}
421+
}
422+
false
423+
}
424+
383425
fabBGLayout.setOnClickListener { closeFloatingActionMenu(applyRiseAndShrinkAnimation = true) }
384426
val addDeckListener =
385427
View.OnClickListener {
@@ -390,6 +432,17 @@ class DeckPickerFloatingActionMenu(
390432
}
391433
addDeckButton.setOnClickListener(addDeckListener)
392434
addDeckLabel.setOnClickListener(addDeckListener)
435+
436+
// Enable keyboard activation for Enter/DPAD_CENTER keys
437+
val addDeckKeyListener =
438+
createActivationKeyListener("Add Deck button: ENTER key pressed") {
439+
if (isFABOpen) {
440+
closeFloatingActionMenu(applyRiseAndShrinkAnimation = false)
441+
deckPicker.showCreateDeckDialog()
442+
}
443+
}
444+
addDeckButton.setOnKeyListener(addDeckKeyListener)
445+
addDeckLabel.setOnKeyListener(addDeckKeyListener)
393446
val addFilteredDeckListener =
394447
View.OnClickListener {
395448
if (isFABOpen) {
@@ -399,6 +452,17 @@ class DeckPickerFloatingActionMenu(
399452
}
400453
addFilteredDeckButton.setOnClickListener(addFilteredDeckListener)
401454
addFilteredDeckLabel.setOnClickListener(addFilteredDeckListener)
455+
456+
// Enable keyboard activation for Enter/DPAD_CENTER keys
457+
val addFilteredDeckKeyListener =
458+
createActivationKeyListener("Add Filtered Deck button: ENTER key pressed") {
459+
if (isFABOpen) {
460+
closeFloatingActionMenu(applyRiseAndShrinkAnimation = false)
461+
deckPicker.showCreateFilteredDeckDialog()
462+
}
463+
}
464+
addFilteredDeckButton.setOnKeyListener(addFilteredDeckKeyListener)
465+
addFilteredDeckLabel.setOnKeyListener(addFilteredDeckKeyListener)
402466
val addSharedListener =
403467
View.OnClickListener {
404468
if (isFABOpen) {
@@ -409,6 +473,17 @@ class DeckPickerFloatingActionMenu(
409473
}
410474
addSharedButton.setOnClickListener(addSharedListener)
411475
addSharedLabel.setOnClickListener(addSharedListener)
476+
477+
// Enable keyboard activation for Enter/DPAD_CENTER keys
478+
val addSharedKeyListener =
479+
createActivationKeyListener("Add Shared Deck button: ENTER key pressed") {
480+
if (isFABOpen) {
481+
closeFloatingActionMenu(applyRiseAndShrinkAnimation = false)
482+
deckPicker.openAnkiWebSharedDecks()
483+
}
484+
}
485+
addSharedButton.setOnKeyListener(addSharedKeyListener)
486+
addSharedLabel.setOnKeyListener(addSharedKeyListener)
412487
val addNoteLabelListener =
413488
View.OnClickListener {
414489
if (isFABOpen) {
@@ -418,6 +493,16 @@ class DeckPickerFloatingActionMenu(
418493
}
419494
}
420495
addNote.setOnClickListener(addNoteLabelListener)
496+
497+
// Enable keyboard activation for Enter/DPAD_CENTER keys
498+
addNote.setOnKeyListener(
499+
createActivationKeyListener("Add Note label: ENTER key pressed") {
500+
if (isFABOpen) {
501+
closeFloatingActionMenu(applyRiseAndShrinkAnimation = false)
502+
addNote()
503+
}
504+
},
505+
)
421506
}
422507

423508
/**

AnkiDroid/src/main/res/layout/floating_add_button.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
android:layout_marginEnd="5dp"
5050
android:layout_height="wrap_content"
5151
android:text="@string/menu_get_shared_decks"
52+
android:focusable="true"
53+
android:clickable="true"
5254
/>
5355
<com.google.android.material.floatingactionbutton.FloatingActionButton
5456
android:id="@+id/add_shared_action"
@@ -59,6 +61,8 @@
5961
app:backgroundTint="?attr/fab_normal"
6062
android:src="@drawable/ic_file_download_white"
6163
android:contentDescription="@string/menu_get_shared_decks"
64+
android:focusable="true"
65+
android:clickable="true"
6266
/>
6367
</LinearLayout>
6468

@@ -86,6 +90,8 @@
8690
android:layout_marginEnd="5dp"
8791
android:layout_height="wrap_content"
8892
android:text="@string/new_dynamic_deck"
93+
android:focusable="true"
94+
android:clickable="true"
8995
/>
9096
<com.google.android.material.floatingactionbutton.FloatingActionButton
9197
android:id="@+id/add_filtered_deck_action"
@@ -96,6 +102,8 @@
96102
app:backgroundTint="?attr/fab_normal"
97103
android:src="@drawable/ic_add_filtered_deck"
98104
android:contentDescription="@string/new_dynamic_deck"
105+
android:focusable="true"
106+
android:clickable="true"
99107
/>
100108
</LinearLayout>
101109

@@ -123,6 +131,8 @@
123131
android:layout_marginEnd="5dp"
124132
android:layout_height="wrap_content"
125133
android:text="@string/new_deck"
134+
android:focusable="true"
135+
android:clickable="true"
126136
/>
127137
<com.google.android.material.floatingactionbutton.FloatingActionButton
128138
android:id="@+id/add_deck_action"
@@ -133,6 +143,8 @@
133143
app:backgroundTint="?attr/fab_normal"
134144
android:src="@drawable/ic_add_deck_filled"
135145
android:contentDescription="@string/new_deck"
146+
android:focusable="true"
147+
android:clickable="true"
136148
/>
137149
</LinearLayout>
138150

@@ -157,6 +169,8 @@
157169
android:text="@string/menu_add"
158170
android:visibility="gone"
159171
android:translationX="180px"
172+
android:focusable="true"
173+
android:clickable="true"
160174
/>
161175
<com.google.android.material.floatingactionbutton.FloatingActionButton
162176
android:id="@+id/fab_main"
@@ -169,6 +183,8 @@
169183
app:srcCompat="@drawable/ic_add_white"
170184
app:backgroundTint="?attr/fab_normal"
171185
app:fabSize="normal"
186+
android:focusable="true"
187+
android:clickable="true"
172188
/>
173189
</LinearLayout>
174190

0 commit comments

Comments
 (0)