Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GSoC'24]: Implement Keyboard Shortcuts Helper #16880

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

SanjaySargam
Copy link
Contributor

Purpose / Description

Keyboard shortcuts for Chromebook users are important because they make it easier and faster to use AnkiDroid. Alt + K shortcut will show keyboard shortcuts dialog

How Has This Been Tested?

HP CHROMEBOOK
Screen recording 2024-08-16 11.38.57 AM.webm

Learning (optional, can help others)

Describe the research stage

_Links to blog posts, patterns, libraries or addons used to solve this problem
https://developer.android.com/develop/ui/compose/touch-input/keyboard-input/keyboard-shortcuts-helper

Checklist

Please, go through these checks before submitting the PR.

  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code
  • UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)
  • UI Changes: You have tested your change using the Google Accessibility Scanner

Copy link
Contributor

Message to maintainers, this PR contains strings changes.

  1. Before merging this PR, it is best to run the "Sync Translations" GitHub action, then make and merge a PR from the i18n_sync branch to get translations cleaned out.
  2. Then merge this PR, and immediately do another translation PR so the huge change made by this PR's key changes are all by themselves.

Read more about updating strings on the wiki,

@SanjaySargam SanjaySargam added Needs Review GSoC Pull requests authored by a Google Summer of Code participant [Candidate/Selected], for GSoC mentors Strings and removed Strings labels Aug 16, 2024
@SanjaySargam SanjaySargam force-pushed the keyboard-shortcut-helper branch 2 times, most recently from ee30b44 to a76f17d Compare August 16, 2024 09:53
AnkiDroid/src/main/java/com/ichi2/compat/Compat.kt Outdated Show resolved Hide resolved
AnkiDroid/src/main/res/values/02-strings.xml Outdated Show resolved Hide resolved
@@ -425,4 +425,35 @@ opening the system text to speech settings fails">
<string name="play_recording">Play</string>
<string name="next_recording">Next</string>

<!--Keyboard Shortcut Dialog-->
<string name="show_keyboard_shortcuts_dialog">Show keyboard shortcuts dialog</string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, because, this is going to be very boring. But can you check that all of those strings are new. If they are not, then please simply consider reusing it.
I would expect that for most case, you'd have an entry in the menus. For example, instead of "Save Note", I think we could reuse "Save" that is already used as a label of the tick in the note editor.

Ideally, I 'd like the strings to be grouped with other strings related to the same feature. E.g. the Open Statistics could be in 10-preferences. After all, I expect that, from a translator point of view (which is what matters here), they should see this string when they want to consider the Preferences.

Finally, I'm going to ask you to add a comment for all of them, because it's probably not clear for translators that this string appear in the context of a shortcut description, or of the category of shortcuts.

It could be simply something such as
comment="Description of the shortcut that open the statistics page"

FYI, "Open statistics" is good, I'm happy that the descriptions are small as much as possible. However, the comment should be very very explicit. Because the translator can't just test the shortcut (as they are not currently using the app, and even if they have a smartphone with ankidroid, they may not even have a keyboard)
And they should be very sure what the shortcut does so that they can find an appropriate short description

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I'll do

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also: do other apps use Title Case, or Sentence case for these strings?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes
Ex: Discord
image

)

val cardTemplateEditorShortcutGroup =
KeyboardShortcutGroup(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed on discord, I'd prefer this to be in a companion object in the CardTemplateEditor. It will be simpler to find where to edit the shortcut content when you edit the code for shortcut.

Also, I'd appreciate if, near the function dealing with shortcut, you can add a comment that state "When you edit this field, reflect the change in cardTemplateEditorShortcutGroup

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed on discord, I'd prefer this to be in a companion object in the CardTemplateEditor. It will be simpler to find where to edit the shortcut content when you edit the code for shortcut.

Is this goes similar to other screens (DeckPicker, NoteEditor, CardBrowser) ??

@david-allison david-allison added the Needs Author Reply Waiting for a reply from the original author label Aug 19, 2024
Copy link
Member

@david-allison david-allison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fundamentally: great change!

AnkiDroid/src/main/java/com/ichi2/compat/CompatV23.kt Outdated Show resolved Hide resolved
menu: Menu?,
deviceId: Int
) {
val shortcutGroups = CompatHelper.compat.getAllShortcuts(this)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we showing shortcuts irrelevant to the current screen?

The reply also be a code comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We include all shortcuts here so that the keyboard shortcut dialog can be opened from anywhere in the app using the Alt+K shortcut, ensuring users have quick access to all available shortcuts regardless of the current screen.

Copy link
Member

@david-allison david-allison Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave this one to @Arthur-Milchior

Personally:

  • If a screen has no specific actions, I'd want everything
  • If a screen has specific actions, I'd only want to see relevant actions + global actions

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops sorry.
In my mind, if a screen has no specific action, I prefer to see only the ankidroid general actions, even if they are only two.
And, most importantly, let's not show the snackbar to tell users to use alt-k. After all, when there is no specific action, this snackbar is pointless

}

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_K && event.isAltPressed) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good start, but realistically nobody is going to know about this keyboard shortcut

There's a number of things we can do here, for example:

  • If a user presses an unmapped shortcut, show a message that "Alt+K" shows all shortcuts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can I get unmapped shortcut? Do you know any easy way to achieve it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically: super is not called if the action is handled

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The snackbar shows up every time any key is pressed, which includes the back and volume buttons.

And why Alt+K? Is that a standard somewhere? In iPadOS it is Command (https://support.apple.com/en-us/102393). Anything that can benefit from external consistency is good to me.

If a user presses an unmapped shortcut, show a message that "Alt+K" shows all shortcuts?

It is useful only once and very annoying every time a miss a shortcut in the future. Alos, that will be a nightmare with controllers, special keys and special devices.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refined the statement in a follow-up comment to only include shortcuts with modifier keys pressed

AnkiDroid/src/main/java/com/ichi2/compat/CompatV23.kt Outdated Show resolved Hide resolved
@@ -425,4 +425,35 @@ opening the system text to speech settings fails">
<string name="play_recording">Play</string>
<string name="next_recording">Next</string>

<!--Keyboard Shortcut Dialog-->
<string name="show_keyboard_shortcuts_dialog">Show keyboard shortcuts dialog</string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also: do other apps use Title Case, or Sentence case for these strings?

AnkiDroid/src/main/java/com/ichi2/compat/CompatV24.kt Outdated Show resolved Hide resolved
AnkiDroid/src/main/java/com/ichi2/compat/CompatV24.kt Outdated Show resolved Hide resolved
AnkiDroid/src/main/res/values/02-strings.xml Outdated Show resolved Hide resolved
AnkiDroid/src/main/res/values/02-strings.xml Show resolved Hide resolved
AnkiDroid/src/main/res/values/02-strings.xml Show resolved Hide resolved
AnkiDroid/src/main/res/values/07-cardbrowser.xml Outdated Show resolved Hide resolved
AnkiDroid/src/main/res/values/07-cardbrowser.xml Outdated Show resolved Hide resolved
AnkiDroid/src/main/res/values/07-cardbrowser.xml Outdated Show resolved Hide resolved
AnkiDroid/src/main/res/values/07-cardbrowser.xml Outdated Show resolved Hide resolved
AnkiDroid/src/main/res/values/07-cardbrowser.xml Outdated Show resolved Hide resolved
menu: Menu?,
deviceId: Int
) {
val shortcutGroups = CompatHelper.compat.getAllShortcuts(this)
Copy link
Member

@david-allison david-allison Aug 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave this one to @Arthur-Milchior

Personally:

  • If a screen has no specific actions, I'd want everything
  • If a screen has specific actions, I'd only want to see relevant actions + global actions

@brishtibheja
Copy link

Is Alt+K used in other Android devices? I couldn't find that on my own. In my Android, the global shortcut is Search+/ and it also works in Google keep notes. The app-specific shortcuts are shown along with the system shortcuts here.

@SanjaySargam
Copy link
Contributor Author

Is Alt+K used in other Android devices?

Nope. Here K for keyboard shortcuts

@brishtibheja
Copy link

It would be nice to have it match with other apps/system shortcuts, although I'm not sure if there's a standard or it just depends on app/device.

@Arthur-Milchior
Copy link
Member

My bad, I'm very sorry, my mistake.
When I saw
image
on https://developer.android.com/develop/ui/compose/touch-input/keyboard-input/keyboard-shortcuts-helper
I thought that "System" "Input" and "Open Apps" were three KeyboardShortcutGroup.

I would have been fine to have such a UI. Let's say you're in card browser, you'll see the CardBrowser group first. And if you wanted, you could open the other groups, to get an idea of what you can do in the deck picker let's say. But by default, you'd only get the currently relevent shortcuts.

Clearly, I was wrong, and the "groups" are all displayed at once in a single list. This indeed looks far too busy. I don't like it.

So, yeah, I'm sorry and I'll ask you to revert and only show the shortcuts from the current activities.

Please also only display the message about shortcut only if there are available shortcuts, so that we don't offer to the user a list of shortcut that only contains alt+k

@SanjaySargam SanjaySargam force-pushed the keyboard-shortcut-helper branch 2 times, most recently from 2f550d3 to 4fc2a14 Compare August 31, 2024 05:51
@SanjaySargam
Copy link
Contributor Author

@Arthur-Milchior

    <string name="deck_picker_widget_description">Deck Picker Widget</string>

OK?

@Arthur-Milchior
Copy link
Member

No. It's clear in the context that it's a widget. No need to state it.
My point was really that it would be useful to add a comment="Title of the Deck Picker Widget. Displayed to the user in Android's Widget Selection menu"

@mikehardy
Copy link
Member

@Arthur-Milchior this appears to have a high frequency of conflicts, and also requires strings
Is it ready to go now or not?

@mikehardy mikehardy added the Review High Priority Request for high priority review label Sep 16, 2024
Copy link
Member

@Arthur-Milchior Arthur-Milchior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikehardy It was not mergeable. If you tried to type an upper case in the note editor, you'd get a toast telling you to use alt+k to see the list of shortcut.

I added two commits on top of it, with my suggestion of change, I hope we save time.

This refactor some code to avoid duplication. And avoid add an extra interface that does not seems very useful to me.
Plus, I ensure that "shift" does not trigger the "show shortcut list" toast

@@ -950,6 +963,12 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su
}
}
}
KeyEvent.KEYCODE_K -> {
if (event.isAltPressed) {
CompatHelper.compat.showKeyboardShortcutsDialog(ankiActivity)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought there was an agreement that, if there is no specific keyboard for an activity A, we'll just show the general shortcuts that are present in all activities.
If so, this part of the code should be in AnkiActivity so that the shortcut alt+k is always available, in all activities.

@@ -961,6 +980,12 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su
selectFieldIndex(humanReadableDigit - 1)
return true
}

// Show snackbar only if a modifier key is pressed and the keyCode is an unmapped alphabet key
if ((event.isCtrlPressed || event.isAltPressed || event.isShiftPressed) && keyCode in KeyEvent.KEYCODE_A..KeyEvent.KEYCODE_Z) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and same with this case.

}

interface DispatchKeyEventListener {
fun dispatchKeyEvent(event: KeyEvent): Boolean
}

interface KeyboardShortcutEventListener {
fun onProvideKeyboardShortcuts(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd appreciate a comment. Even if it's just @see and the function you imitate

val label: String = label.toDisplayString(context)
val parts = shortcut.split("+")
val key = parts.last()
val keycode: Int = KeyEvent.keyCodeFromString(key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This require the user to type "NUMPAD_1" and so on for number.
I suggest we have our own function getKey, similar to getModifier, that will return the keycode of a given key.
Except that the key could be a single digit, so we don't have to write "NUMPAD_1" everytime we use a number in a shortcut. (Admittedly, right now, this only concerns the reviewer. Still, I'd find it more readable in the reviewer code)

@@ -321,62 +326,93 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
invalidateOptionsMenu()
}

override fun onProvideKeyboardShortcuts(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me this could be in AnkiActivity, so we don't have to copy-paste it in every child

return true
}

// Show snackbar only if a modifier key is pressed and the keyCode is an unmapped alphabet key
if ((event.isCtrlPressed || event.isAltPressed || event.isShiftPressed) && keyCode in KeyEvent.KEYCODE_A..KeyEvent.KEYCODE_Z) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion:

  • alt+k should work in every activity. In the worst case, the user will see there is not a lot to do there
  • the snackbar should be shown iff there are anything specific to see there. So we don't invite the user to open the shortcut list uselessly. It can be done simply by checking whether shortcuts is null
  • on top of A-Z we should also consider digits.

if (fragment is KeyboardShortcutEventListener) {
fragment.onProvideKeyboardShortcuts(data, menu, deviceId)
} else {
super.onProvideKeyboardShortcuts(data, menu, deviceId)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should call super in any case.

@@ -804,6 +822,12 @@ open class CardBrowser :
}
}
}

// Show snackbar only if a modifier key is pressed and the keyCode is an unmapped alphabet key
if ((event.isCtrlPressed || event.isAltPressed || event.isShiftPressed) && keyCode in KeyEvent.KEYCODE_A..KeyEvent.KEYCODE_Z) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shift part should be removed I think. Because it's naturally used when typing in the search bar, and it ends up with the message appearing each time you put a upper case character

import com.ichi2.anki.CollectionManager.TR
import net.ankiweb.rsdroid.Translations

sealed interface StringResource {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a huge fan of this interface.
Either we expect to have similar need into the future, in which case I believe it should also accept strings with parameters and be fully general.
Or we expect it to be only for Shortcut, in which case

/**
 * Provides a [CompatV24.Shortcut], from the shortcut keys and the resource id of its description.
 */
fun AnkiActivityProvider.shortcut(shortcut: String, @StringRes labelRes: Int) =
    CompatV24.Shortcut(shortcut, ankiActivity.getString(labelRes))

/**
 * Provides a [CompatV24.Shortcut], from the shortcut keys and the function from anki strings.
 */
fun shortcut(shortcut: String, getTranslation: Translations.() -> String) =
    CompatV24.Shortcut(shortcut, getTranslation(TR))

is largely sufficient.

With

interface AnkiActivityProvider {
    val ankiActivity: AnkiActivity
}

@Arthur-Milchior Arthur-Milchior added the squash-merge The pull request currently requires maintainers to "Squash Merge" label Sep 21, 2024
Copy link
Member

@Arthur-Milchior Arthur-Milchior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@david-allison there was quite some changes since you reviewed. Including one that corrected a real bug. So I'd appreciate if you could re-review before we merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
GSoC Pull requests authored by a Google Summer of Code participant [Candidate/Selected], for GSoC mentors Has Conflicts Needs Second Approval Has one approval, one more approval to merge Review High Priority Request for high priority review squash-merge The pull request currently requires maintainers to "Squash Merge" Strings
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants