Skip to content

Commit

Permalink
feat: keyboard shortcuts helper
Browse files Browse the repository at this point in the history
  • Loading branch information
SanjaySargam committed Aug 24, 2024
1 parent 92f4d1c commit 508aa7b
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 0 deletions.
30 changes: 30 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import android.media.AudioManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.KeyboardShortcutGroup
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
Expand All @@ -39,6 +42,7 @@ import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.snackbar.Snackbar
import com.ichi2.anim.ActivityTransitionAnimation
import com.ichi2.anim.ActivityTransitionAnimation.Direction
import com.ichi2.anim.ActivityTransitionAnimation.Direction.DEFAULT
Expand All @@ -54,6 +58,8 @@ import com.ichi2.anki.preferences.sharedPrefs
import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.anki.workarounds.AppLoadedFromBackupWorkaround.showedActivityFailedScreen
import com.ichi2.async.CollectionLoader
import com.ichi2.compat.CompatHelper
import com.ichi2.compat.CompatHelper.Companion.showKeyboardShortcutsDialog
import com.ichi2.compat.customtabs.CustomTabActivityHelper
import com.ichi2.compat.customtabs.CustomTabsFallback
import com.ichi2.compat.customtabs.CustomTabsHelper
Expand Down Expand Up @@ -102,6 +108,30 @@ open class AnkiActivity : AppCompatActivity, SimpleMessageDialogListener {
}
}

override fun onProvideKeyboardShortcuts(
data: MutableList<KeyboardShortcutGroup>,
menu: Menu?,
deviceId: Int
) {
// 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.
val shortcutGroups = CompatHelper.compat.getAllShortcuts(this)
data.addAll(shortcutGroups)
super.onProvideKeyboardShortcuts(data, menu, deviceId)
}

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_K && event.isAltPressed) {
// Alt+K: Show keyboard shortcuts dialog
showKeyboardShortcutsDialog()
return true
} else {
// Show snackbar message for unmapped key codes
showSnackbar(R.string.show_shortcuts_message, Snackbar.LENGTH_SHORT)
}
return super.onKeyUp(keyCode, event)
}

override fun onStart() {
super.onStart()
customTabActivityHelper.bindCustomTabsService(this)
Expand Down
34 changes: 34 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ import com.ichi2.anki.widgets.DeckDropDownAdapter.SubtitleListener
import com.ichi2.annotations.NeedsTest
import com.ichi2.async.renderBrowserQA
import com.ichi2.compat.CompatHelper.Companion.registerReceiverCompat
import com.ichi2.compat.CompatV24.Shortcut
import com.ichi2.libanki.Card
import com.ichi2.libanki.CardId
import com.ichi2.libanki.ChangeManager
Expand Down Expand Up @@ -147,6 +148,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import net.ankiweb.rsdroid.RustCleanup
import net.ankiweb.rsdroid.Translations
import timber.log.Timber
import kotlin.math.abs
import kotlin.math.ceil
Expand Down Expand Up @@ -2387,6 +2389,38 @@ open class CardBrowser :
// Values related to persistent state data
private const val ALL_DECKS_ID = 0L

val shortcuts = listOf(
Shortcut("Ctrl+Shift+A", R.string.edit_tags_dialog),
Shortcut("Ctrl+A", R.string.card_browser_select_all),
Shortcut("Ctrl+Shift+E", Translations::exportingExport),
Shortcut("Ctrl+E", R.string.menu_add_note),
Shortcut("E", R.string.cardeditor_title_edit_card),
Shortcut("Ctrl+D", R.string.card_browser_change_deck),
Shortcut("Ctrl+K", Translations::browsingToggleMark),
Shortcut("Ctrl+Alt+R", Translations::browsingReschedule),
Shortcut("DEL", R.string.delete_card_title),
Shortcut("Ctrl+Alt+N", R.string.reset_card_dialog_title),
Shortcut("Ctrl+Alt+T", R.string.toggle_cards_notes),
Shortcut("T", R.string.card_browser_search_by_tag),
Shortcut("Ctrl+Shift+S", Translations::actionsReposition),
Shortcut("Ctrl+Alt+S", R.string.card_browser_list_my_searches),
Shortcut("Ctrl+S", R.string.card_browser_list_my_searches_save),
Shortcut("Alt+S", R.string.card_browser_show_suspended),
Shortcut("Ctrl+Shift+J", Translations::browsingToggleBury),
Shortcut("Ctrl+J", Translations::browsingToggleSuspend),
Shortcut("Ctrl+Shift+I", Translations::actionsCardInfo),
Shortcut("Ctrl+O", R.string.show_order_dialog),
Shortcut("Ctrl+M", R.string.card_browser_show_marked),
Shortcut("ESCAPE", R.string.card_browser_select_none),
Shortcut("Ctrl+NUMPAD_1", R.string.gesture_flag_red),
Shortcut("Ctrl+NUMPAD_2", R.string.gesture_flag_orange),
Shortcut("Ctrl+NUMPAD_3", R.string.gesture_flag_green),
Shortcut("Ctrl+NUMPAD_4", R.string.gesture_flag_blue),
Shortcut("Ctrl+NUMPAD_5", R.string.gesture_flag_pink),
Shortcut("Ctrl+NUMPAD_6", R.string.gesture_flag_turquoise),
Shortcut("Ctrl+NUMPAD_7", R.string.gesture_flag_purple)
)

fun clearLastDeckId() = SharedPreferencesLastDeckIdRepository.clearLastDeckId()

@VisibleForTesting
Expand Down
17 changes: 17 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import com.ichi2.anki.utils.ext.isImageOcclusion
import com.ichi2.anki.utils.postDelayed
import com.ichi2.annotations.NeedsTest
import com.ichi2.compat.CompatHelper.Companion.getSerializableCompat
import com.ichi2.compat.CompatV24.Shortcut
import com.ichi2.libanki.Collection
import com.ichi2.libanki.Note
import com.ichi2.libanki.NoteId
Expand All @@ -86,6 +87,7 @@ import com.ichi2.ui.FixedTextView
import com.ichi2.utils.KotlinCleanup
import com.ichi2.utils.copyToClipboard
import com.ichi2.utils.jsonObjectIterable
import net.ankiweb.rsdroid.Translations
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
Expand Down Expand Up @@ -1244,5 +1246,20 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {

@Suppress("unused")
private const val REQUEST_CARD_BROWSER_APPEARANCE = 1

val shortcuts = listOf(
Shortcut("Ctrl+P", R.string.card_editor_preview_card),
Shortcut("Ctrl+NUMPAD_1", R.string.edit_front),
Shortcut("Ctrl+NUMPAD_2", R.string.edit_back),
Shortcut("Ctrl+NUMPAD_3", R.string.edit_styling),
Shortcut("Ctrl+S", R.string.save),
Shortcut("Ctrl+I", R.string.card_template_editor_insert_field),
Shortcut("Ctrl+A", Translations::cardTemplatesAddCardType),
Shortcut("Ctrl+R", Translations::cardTemplatesRenameCardType),
Shortcut("Ctrl+B", R.string.card_template_browser_appearance_title),
Shortcut("Ctrl+D", Translations::cardTemplatesRemoveCardType),
Shortcut("Ctrl+O", Translations::cardTemplatesDeckOverride),
Shortcut("Ctrl+M", R.string.copy_as_markdown)
)
}
}
23 changes: 23 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ import com.ichi2.compat.CompatHelper
import com.ichi2.compat.CompatHelper.Companion.getSerializableCompat
import com.ichi2.compat.CompatHelper.Companion.registerReceiverCompat
import com.ichi2.compat.CompatHelper.Companion.sdkVersion
import com.ichi2.compat.CompatV24.Shortcut
import com.ichi2.libanki.ChangeManager
import com.ichi2.libanki.Consts
import com.ichi2.libanki.DeckId
Expand Down Expand Up @@ -210,6 +211,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import makeLinksClickable
import net.ankiweb.rsdroid.RustCleanup
import net.ankiweb.rsdroid.Translations
import org.json.JSONException
import timber.log.Timber
import java.io.File
Expand Down Expand Up @@ -2678,6 +2680,27 @@ open class DeckPicker :
private const val AUTOMATIC_SYNC_MINIMAL_INTERVAL_IN_MINUTES: Long = 10
private const val SWIPE_TO_SYNC_TRIGGER_DISTANCE = 400

val shortcuts = listOf(
Shortcut("A", R.string.menu_add_note),
Shortcut("Ctrl+B", R.string.backup_restore),
Shortcut("B", R.string.card_browser_context_menu),
Shortcut("Y", R.string.pref_cat_sync),
Shortcut("SLASH", R.string.deck_conf_cram_search),
Shortcut("S", Translations::decksStudyDeck),
Shortcut("T", R.string.open_statistics),
Shortcut("C", R.string.check_db),
Shortcut("D", R.string.new_deck),
Shortcut("F", R.string.new_dynamic_deck),
Shortcut("Shift+DEL", R.string.delete_deck_without_confirmation),
Shortcut("DEL", R.string.delete_deck_title),
Shortcut("R", R.string.rename_deck),
Shortcut("P", R.string.open_settings),
Shortcut("M", R.string.check_media),
Shortcut("Ctrl+E", R.string.export_collection),
Shortcut("Ctrl+Shift+I", R.string.menu_import),
Shortcut("Ctrl+Shift+N", R.string.model_browser_label)
)

// Animation utility methods used by renderPage() method
fun fadeIn(view: View?, duration: Int, translation: Float = 0f, startAction: Runnable? = Runnable { view!!.visibility = View.VISIBLE }): ViewPropertyAnimator {
view!!.alpha = 0f
Expand Down
11 changes: 11 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ import com.ichi2.annotations.NeedsTest
import com.ichi2.compat.CompatHelper
import com.ichi2.compat.CompatHelper.Companion.getSerializableCompat
import com.ichi2.compat.CompatHelper.Companion.registerReceiverCompat
import com.ichi2.compat.CompatV24.Shortcut
import com.ichi2.libanki.Card
import com.ichi2.libanki.Collection
import com.ichi2.libanki.Consts
Expand Down Expand Up @@ -2677,6 +2678,16 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su
private const val PREF_NOTE_EDITOR_FONT_SIZE = "note_editor_font_size"
private const val PREF_NOTE_EDITOR_CUSTOM_BUTTONS = "note_editor_custom_buttons"

val shortcuts = listOf(
Shortcut("Ctrl+ENTER", R.string.save),
Shortcut("Ctrl+D", R.string.deck_selection_dialog),
Shortcut("Ctrl+L", R.string.card_template_editor_group),
Shortcut("Ctrl+N", R.string.note_selection_spinner),
Shortcut("Ctrl+Shift+T", R.string.tags_dialog),
Shortcut("Ctrl+Shift+C", R.string.multimedia_editor_popup_cloze),
Shortcut("Ctrl+P", R.string.card_editor_preview_card)
)

private fun shouldReplaceNewlines(): Boolean {
return AnkiDroidApp.instance.sharedPrefs()
.getBoolean(PREF_NOTE_EDITOR_NEWLINE_REPLACE, true)
Expand Down
13 changes: 13 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/compat/Compat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import android.graphics.Bitmap.CompressFormat
import android.media.MediaRecorder
import android.net.Uri
import android.os.Bundle
import android.view.KeyboardShortcutGroup
import android.view.View
import androidx.annotation.CheckResult
import androidx.core.view.OnReceiveContentListener
Expand Down Expand Up @@ -218,6 +219,18 @@ interface Compat {
onReceiveContentListener: OnReceiveContentListener
)

/**
* @see CompatHelper.showKeyboardShortcutsDialog
*/
fun showKeyboardShortcutsDialog(
activity: Activity
)

/**
* Get all keyboard shortcuts
*/
fun getAllShortcuts(activity: Activity): List<KeyboardShortcutGroup>

/**
* Converts a locale to a 'two letter' code (ISO-639-1 + ISO 3166-1 alpha-2)
* Locale("spa", "MEX", "001") => Locale("es", "MX", "001")
Expand Down
6 changes: 6 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/compat/CompatHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import android.view.KeyCharacterMap.deviceHasKey
import android.view.KeyEvent.KEYCODE_PAGE_DOWN
import android.view.KeyEvent.KEYCODE_PAGE_UP
import androidx.core.content.ContextCompat
import com.ichi2.anki.AnkiActivity
import com.ichi2.compat.CompatHelper.Companion.compat
import java.io.Serializable

Expand Down Expand Up @@ -193,5 +194,10 @@ class CompatHelper private constructor() {
@ContextCompat.RegisterReceiverFlags flags: Int
) =
ContextCompat.registerReceiver(this, receiver, filter, flags)

/**
* Shows keyboard shortcuts dialog
*/
fun AnkiActivity.showKeyboardShortcutsDialog() = compat.showKeyboardShortcutsDialog(this)
}
}
12 changes: 12 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/compat/CompatV23.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import android.os.Bundle
import android.os.Environment
import android.os.Vibrator
import android.provider.MediaStore
import android.view.KeyboardShortcutGroup
import android.view.View
import androidx.appcompat.widget.TooltipCompat
import androidx.core.view.OnReceiveContentListener
Expand Down Expand Up @@ -161,6 +162,17 @@ open class CompatV23 : Compat {
// No implementation possible.
}

// Until API 24
override fun showKeyboardShortcutsDialog(activity: Activity) {
// No implementation available
}

// Until API 24
override fun getAllShortcuts(activity: Activity): List<KeyboardShortcutGroup> {
// No implementation available
return mutableListOf()
}

// Until API 26
@Throws(IOException::class)
override fun deleteFile(file: File) {
Expand Down
Loading

0 comments on commit 508aa7b

Please sign in to comment.