Skip to content

ADFA-3153 | Introduce modular Code On the Go shortcut system#1083

Merged
jatezzz merged 5 commits intostagefrom
feat/ADFA-3153-ide-shortcut-system
Mar 20, 2026
Merged

ADFA-3153 | Introduce modular Code On the Go shortcut system#1083
jatezzz merged 5 commits intostagefrom
feat/ADFA-3153-ide-shortcut-system

Conversation

@jatezzz
Copy link
Collaborator

@jatezzz jatezzz commented Mar 16, 2026

Description

This PR introduces a modular keyboard shortcut system for COGO.
The implementation organizes shortcuts into logical groups, centralizes their registration in a catalog, and adds a structured execution layer capable of resolving actions through the IDE action registry with built-in fallbacks.

The editor activity is now integrated with the shortcut dispatcher, enabling context-aware shortcut handling (e.g., editor, main screen, modal dialogs).

Details

Key improvements included in this PR:

  • Introduced ShortcutGroup architecture to organize shortcuts by functional areas.

  • Added grouped shortcut implementations:

    • ProjectsAndSolutionsGroup
    • WindowsAndDisplayGroup
    • SearchAndReplaceGroup
    • FileManagementGroup (referenced in provider)
  • Implemented ShortcutCatalog for centralized aggregation of shortcuts.

  • Added ShortcutCategory and ShortcutContext enums for classification and contextual filtering.

  • Implemented ShortcutGroupProvider to register all available groups.

  • Introduced IdeShortcutActions executor to:

    • Resolve actions via the COGO ActionsRegistry
    • Provide fallback execution paths when actions are unavailable
  • Added ShortcutActionIds as a central registry for built-in COGO shortcut actions.

  • Added ShortcutExecutionContext abstraction for passing execution dependencies.

  • Integrated shortcut dispatching into EditorHandlerActivity via dispatchKeyEvent.

  • Added support for handling modal dismissal using ESC.

This structure enables future shortcut additions without modifying core shortcut dispatch logic.

document_5019473586721654554.1.mp4

Ticket

ADFA-3153

Observation

The shortcut system is designed to be extensible by introducing new ShortcutGroup implementations without modifying the central catalog logic.
Fallback execution ensures that essential COGO behaviors remain functional even if an action is not registered in the ActionsRegistry.

@jatezzz jatezzz requested a review from a team March 16, 2026 21:35
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 16, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a keyboard shortcut system and integrates it into the app; introduces ShortcutManager, definitions, groups, and execution plumbing; wires shortcut dispatch into MainActivity and EditorHandlerActivity; replaces MainScreenAction with ActionItem-based registry and adds main-screen actions and utilities.

Changes

Cohort / File(s) Summary
Activities – shortcut wiring
app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt, app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt
Added dispatchKeyEvent overrides, lazy ShortcutManager and execution contexts, registered/cleared MainScreenActions, and new public methods for create/open/clone project flows.
Shortcut core
app/src/main/java/com/itsaky/androidide/shortcuts/*
New shortcut framework: ShortcutManager, ShortcutCatalog, ShortcutDefinition, KeyShortcut, IdeShortcutActions, ShortcutExecutionContext, ShortcutContext, ShortcutCategory. Dispatch, matching, prioritization, and action execution implemented.
Shortcut groups & IDs
app/src/main/java/com/itsaky/androidide/shortcuts/groups/*, .../ShortcutDefinitionIds.kt
Added ShortcutGroup interface and four group implementations returning ShortcutDefinition instances; centralized shortcut ID constants.
Main screen actions & registry
app/src/main/java/com/itsaky/androidide/actions/main/*.kt, app/src/main/java/com/itsaky/androidide/utils/MainScreenActions.kt
Added multiple ActionItem implementations (Create/Open/Clone/Delete/Terminal/Preferences/Docs/Donate) and utility to register/clear MAIN_SCREEN actions in ActionsRegistry.
Action ID extraction
app/src/main/java/com/itsaky/androidide/actions/file/CloseFileAction.kt, app/src/main/java/com/itsaky/androidide/actions/sidebar/PreferencesSidebarAction.kt
Replaced hard-coded id literals with public companion object ID constants and bound overrides to those constants.
Action item location
actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt
Added new Location.MAIN_SCREEN enum constant.
UI adapter & fragment refactor
app/src/main/java/com/itsaky/androidide/adapters/MainActionsListAdapter.kt, app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt, app/src/main/java/com/itsaky/androidide/models/MainScreenAction.kt
Removed legacy MainScreenAction model; adapter now accepts List<ActionItem> with click/long-click callbacks; MainFragment refactored to use ActionsRegistry and centralized dispatch.
Dialog utilities
app/src/main/java/com/itsaky/androidide/utils/FragmentUtils.kt
Added FragmentManager extensions hasVisibleDialog() and dismissTopDialog() for modal detection/dismissal.
Resources
resources/src/main/res/values/strings.xml
Added string resources for shortcut labels/titles.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Activity as MainActivity/<br/>EditorHandlerActivity
    participant SM as ShortcutManager
    participant Catalog as ShortcutCatalog
    participant Exec as ShortcutExecutionContext
    participant IdeActions as IdeShortcutActions
    participant Registry as ActionsRegistry
    participant Action as ActionItem

    User->>Activity: press key
    Activity->>Activity: dispatchKeyEvent(event)
    Activity->>SM: dispatch(event, context, focusView, executionContext, hasModal)
    SM->>Catalog: all(context)
    Catalog-->>SM: definitions
    SM->>SM: findMatchingShortcut(event, activeContexts)
    SM->>Exec: execute matched definition
    Exec->>IdeActions: execute(actionId)
    IdeActions->>Registry: findActionById(actionId)
    Registry-->>IdeActions: ActionItem
    IdeActions->>Action: prepare(data) + execAction(data)
    Action-->>IdeActions: result
    IdeActions-->>Exec: success/failure
    Exec-->>SM: propagate result
    SM-->>Activity: handled? 
    alt handled
        Activity-->>User: action executed
    else not handled
        Activity->>Activity: super.dispatchKeyEvent()
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • itsaky-adfa

Poem

🐰
I hopped through keys and tied a ribbon tight,
Now ctrl and shift make widgets take to flight,
Registry hums, shortcuts leap with glee,
Dialogs hide — the keys set tasks free,
A twitch of whiskers — IDE delight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: introducing a modular keyboard shortcut system for Code On the Go (COGO).
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing the shortcut system architecture, key improvements, integrated components, and design principles.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ADFA-3153-ide-shortcut-system
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutGroupProvider.kt (1)

13-18: Cache group instances instead of recreating them on every all() call.

These groups are stateless, so rebuilding the list each call adds unnecessary allocations.

♻️ Proposed refactor
 class ShortcutGroupProvider {
-	fun all(): List<ShortcutGroup> = listOf(
-		ProjectsAndSolutionsGroup(),
-		WindowsAndDisplayGroup(),
-		FileManagementGroup(),
-		SearchAndReplaceGroup(),
-	)
+	private val groups: List<ShortcutGroup> = listOf(
+		ProjectsAndSolutionsGroup(),
+		WindowsAndDisplayGroup(),
+		FileManagementGroup(),
+		SearchAndReplaceGroup(),
+	)
+
+	fun all(): List<ShortcutGroup> = groups
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutGroupProvider.kt`
around lines 13 - 18, The all() function currently constructs new ShortcutGroup
instances on every call causing needless allocations; change
ShortcutGroupProvider to create and store a single immutable list of those
stateless groups (e.g., a private val like cachedGroups =
listOf(ProjectsAndSolutionsGroup(), WindowsAndDisplayGroup(),
FileManagementGroup(), SearchAndReplaceGroup())) and have fun all():
List<ShortcutGroup> return that cached list instead of rebuilding it each time.
app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt (1)

145-151: Avoid recreating execution objects on every key event.

editorShortcutExecutionContext() allocates new ShortcutExecutionContext and IdeShortcutActions for each dispatch; this can be a cached property since the provider lambda already resolves fresh ActionData.

♻️ Proposed refactor
-	private fun editorShortcutExecutionContext(): ShortcutExecutionContext {
-		return ShortcutExecutionContext(
-			ideShortcutActions = IdeShortcutActions {
-				createToolbarActionData()
-			},
-		)
-	}
+	private val editorShortcutExecutionContext by lazy {
+		ShortcutExecutionContext(
+			ideShortcutActions = IdeShortcutActions {
+				createToolbarActionData()
+			},
+		)
+	}
-			executionContext = editorShortcutExecutionContext(),
+			executionContext = editorShortcutExecutionContext,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt`
around lines 145 - 151, The method editorShortcutExecutionContext() currently
constructs a new ShortcutExecutionContext and IdeShortcutActions on every call;
change this to a cached property (e.g., a private val or lazy-initialized field)
that holds a single ShortcutExecutionContext instance so it isn't reallocated
per key event, while keeping the IdeShortcutActions provider lambda calling
createToolbarActionData() unchanged; update usages to reference the cached
property instead of calling editorShortcutExecutionContext() repeatedly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/shortcuts/groups/FileManagementGroup.kt`:
- Around line 16-27: The shortcut ID ShortcutDefinitionIds.SAVE_FILE is
misleading because SaveFileAction.ID ("ide.editor.files.saveAll") and its
implementation (context.saveAllResult(), UI string "all_saved") save all open
files; either rename the constant to SAVE_ALL_FILES and update all references to
use ShortcutDefinitionIds.SAVE_ALL_FILES and the ShortcutDefinition id, or keep
the ID but update the shortcut title/tooltip/documentation to explicitly say
"Save all files" (and adjust any string resources if needed) so the Ctrl+S
binding and SaveFileAction.ID consistently communicate that it saves all open
files.

In
`@app/src/main/java/com/itsaky/androidide/shortcuts/groups/SearchAndReplaceGroup.kt`:
- Around line 16-39: The two ShortcutDefinition entries for
ShortcutDefinitionIds.FIND_IN_PROJECT and ShortcutDefinitionIds.FIND_IN_FILE
have their KeyShortcut bindings inverted; change the FIND_IN_PROJECT entry to
use KeyShortcut.ctrlShift(KeyEvent.KEYCODE_F) and change the FIND_IN_FILE entry
to use KeyShortcut.ctrl(KeyEvent.KEYCODE_F) so Ctrl+F triggers
FindInFileAction.ID and Ctrl+Shift+F triggers FindInProjectAction.ID, keeping
titles and actionId values (FindInProjectAction.ID, FindInFileAction.ID) as-is.

In `@app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutManager.kt`:
- Around line 48-63: The priority calculation in findMatchingShortcut is using
definition.contexts (which may include inactive contexts) when calling
priorityScore; update the scoring to only consider the intersection of
definition.contexts with the provided activeContexts (e.g., compute active =
definition.contexts.filter { it in activeContexts } or
.intersect(activeContexts)) and then use active.maxOfOrNull { shortcutContext ->
priorityScore(shortcutContext) } ?: Int.MIN_VALUE so inactive contexts (like
MODAL when not active) do not inflate a shortcut's priority.

---

Nitpick comments:
In
`@app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt`:
- Around line 145-151: The method editorShortcutExecutionContext() currently
constructs a new ShortcutExecutionContext and IdeShortcutActions on every call;
change this to a cached property (e.g., a private val or lazy-initialized field)
that holds a single ShortcutExecutionContext instance so it isn't reallocated
per key event, while keeping the IdeShortcutActions provider lambda calling
createToolbarActionData() unchanged; update usages to reference the cached
property instead of calling editorShortcutExecutionContext() repeatedly.

In `@app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutGroupProvider.kt`:
- Around line 13-18: The all() function currently constructs new ShortcutGroup
instances on every call causing needless allocations; change
ShortcutGroupProvider to create and store a single immutable list of those
stateless groups (e.g., a private val like cachedGroups =
listOf(ProjectsAndSolutionsGroup(), WindowsAndDisplayGroup(),
FileManagementGroup(), SearchAndReplaceGroup())) and have fun all():
List<ShortcutGroup> return that cached list instead of rebuilding it each time.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c18d95fe-bef9-4470-90a5-045746c5c72c

📥 Commits

Reviewing files that changed from the base of the PR and between b5b55d4 and 98a287d.

📒 Files selected for processing (22)
  • app/src/main/java/com/itsaky/androidide/actions/file/CloseFileAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/sidebar/PreferencesSidebarAction.kt
  • app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/IdeShortcutActions.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/KeyShortcut.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutActionIds.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutCatalog.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutCategory.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutContext.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutDefinition.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutExecutionContext.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutGroupProvider.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutManager.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/FileManagementGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ProjectsAndSolutionsGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/SearchAndReplaceGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ShortcutDefinitionIds.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ShortcutGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/WindowsAndDisplayGroup.kt
  • app/src/main/java/com/itsaky/androidide/utils/FragmentUtils.kt
  • resources/src/main/res/values/strings.xml

@jatezzz jatezzz force-pushed the feat/ADFA-3153-ide-shortcut-system branch from f0fc199 to a4b67d4 Compare March 17, 2026 13:31
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/src/main/java/com/itsaky/androidide/utils/FragmentUtils.kt (1)

6-17: Consider extracting shared fragment-tree traversal logic.

hasVisibleDialog() and dismissTopDialog() duplicate near-identical DFS traversal. A shared private walker/helper would reduce drift between detection and dismissal behavior.

Also applies to: 19-33

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/utils/FragmentUtils.kt` around lines
6 - 17, Both hasVisibleDialog() and dismissTopDialog() implement the same DFS
over FragmentManager fragments; extract a single private helper (e.g.,
FragmentManager.walkFragments / FragmentManager.findInFragments or
FragmentUtils.traverseFragmentTree) that performs recursive traversal and
accepts a lambda predicate/action to avoid duplication, then rewrite
hasVisibleDialog() to call the helper with a predicate checking "fragment is
DialogFragment && fragment.dialog?.isShowing == true" and rewrite
dismissTopDialog() to call the same helper to locate and dismiss the dialog
fragment; ensure the helper uses fragment.childFragmentManager recursion and
returns early when the predicate/action signals a match.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/com/itsaky/androidide/utils/FragmentUtils.kt`:
- Around line 12-13: Guard access to fragment.childFragmentManager by checking
fragment.isAdded before recursing into child fragments: wherever you call
fragment.childFragmentManager.hasVisibleDialog() (both occurrences that
currently access childFragmentManager unconditionally), first verify
fragment.isAdded and only then call childFragmentManager.hasVisibleDialog();
this prevents IllegalStateException for unattached fragments while preserving
the existing recursion/return logic.

---

Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/utils/FragmentUtils.kt`:
- Around line 6-17: Both hasVisibleDialog() and dismissTopDialog() implement the
same DFS over FragmentManager fragments; extract a single private helper (e.g.,
FragmentManager.walkFragments / FragmentManager.findInFragments or
FragmentUtils.traverseFragmentTree) that performs recursive traversal and
accepts a lambda predicate/action to avoid duplication, then rewrite
hasVisibleDialog() to call the helper with a predicate checking "fragment is
DialogFragment && fragment.dialog?.isShowing == true" and rewrite
dismissTopDialog() to call the same helper to locate and dismiss the dialog
fragment; ensure the helper uses fragment.childFragmentManager recursion and
returns early when the predicate/action signals a match.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ad1f65f0-ec77-4c2b-b2aa-a82ad769f00e

📥 Commits

Reviewing files that changed from the base of the PR and between f0fc199 and a4b67d4.

📒 Files selected for processing (22)
  • app/src/main/java/com/itsaky/androidide/actions/file/CloseFileAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/sidebar/PreferencesSidebarAction.kt
  • app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/IdeShortcutActions.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/KeyShortcut.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutActionIds.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutCatalog.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutCategory.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutContext.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutDefinition.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutExecutionContext.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutGroupProvider.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutManager.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/FileManagementGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ProjectsAndSolutionsGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/SearchAndReplaceGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ShortcutDefinitionIds.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ShortcutGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/WindowsAndDisplayGroup.kt
  • app/src/main/java/com/itsaky/androidide/utils/FragmentUtils.kt
  • resources/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (19)
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ShortcutGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ProjectsAndSolutionsGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutManager.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutDefinition.kt
  • resources/src/main/res/values/strings.xml
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ShortcutDefinitionIds.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/IdeShortcutActions.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutCategory.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutGroupProvider.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutExecutionContext.kt
  • app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/SearchAndReplaceGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutContext.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutActionIds.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/KeyShortcut.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/FileManagementGroup.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutCatalog.kt
  • app/src/main/java/com/itsaky/androidide/actions/sidebar/PreferencesSidebarAction.kt

@davidschachterADFA davidschachterADFA changed the title ADFA-3153 | Introduce modular COGO shortcut system ADFA-3153 | Introduce modular Code On the Go shortcut system Mar 17, 2026
@jatezzz jatezzz requested a review from itsaky-adfa March 18, 2026 14:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
app/src/main/java/com/itsaky/androidide/actions/main/DonateAction.kt (1)

29-35: Collapse the dead visibility branch.

Line 34 hides the action unconditionally, so the null-context check on Lines 31-33 never changes behavior. Removing it would make the temporary disablement clearer.

✂️ Suggested cleanup
     override fun prepare(data: ActionData) {
         super.prepare(data)
-        if (data.get(Context::class.java) == null) {
-            markInvisible()
-        }
         markInvisible() // Until we allow donations
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/actions/main/DonateAction.kt` around
lines 29 - 35, In DonateAction.prepare(ActionData) remove the dead conditional
that checks data.get(Context::class.java) for null since markInvisible() is
called unconditionally; delete the if block (the null-context check and its
markInvisible() call) so the method only calls super.prepare(data) and the
single temporary markInvisible(), keeping DonateAction and prepare as the points
to edit.
app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt (1)

54-58: Avoid hard-wiring DefaultActionsRegistry into the fragment.

This screen already depends on ActionsRegistry, but the concrete type check means any alternative implementation or test double turns clicks into no-ops. Moving execution behind the registry API, or a dedicated executor, would keep the new shortcut/action flow extensible.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt` around
lines 54 - 58, The fragment currently hard-checks registry is a
DefaultActionsRegistry in the onClick handler which prevents other
ActionsRegistry implementations from executing actions; remove the
DefaultActionsRegistry type check and invoke the execute method via the registry
interface (or inject a small ActionExecutor and call its execute(action,
actionData) from the onClick lambda). If the ActionsRegistry interface lacks an
executeAction(ActionItem, actionData) method, add that method to the interface
(or add the new ActionExecutor abstraction) and update callers to use the
interface method rather than casting to DefaultActionsRegistry; keep references
to onClick, ActionItem, registry, executeAction and actionData to locate the
change.
app/src/main/java/com/itsaky/androidide/actions/main/OpenProjectAction.kt (1)

13-43: Consider extracting a shared base for main-screen navigation actions.

This class duplicates the same scaffolding pattern used in CreateProjectAction and CloneRepositoryAction (metadata setup + MainActivity gating + safe execution). A small abstract base would reduce drift and keep behavior consistent.

Refactor sketch
+abstract class MainScreenNavigationAction(
+    context: Context,
+    final override val id: String,
+    labelRes: Int,
+    iconRes: Int,
+    final override val order: Int,
+) : ActionItem {
+    final override var label: String = context.getString(labelRes)
+    final override var visible: Boolean = true
+    final override var enabled: Boolean = true
+    final override var icon: Drawable? = ContextCompat.getDrawable(context, iconRes)
+    final override var requiresUIThread: Boolean = true
+    final override var location: ActionItem.Location = ActionItem.Location.MAIN_SCREEN
+
+    override fun prepare(data: ActionData) {
+        super.prepare(data)
+        if (data.get(Context::class.java) !is MainActivity) {
+            markInvisible()
+        }
+    }
+}
-class OpenProjectAction(context: Context) : ActionItem {
+class OpenProjectAction(context: Context) : MainScreenNavigationAction(
+    context = context,
+    id = ID,
+    labelRes = R.string.msg_open_existing_project,
+    iconRes = R.drawable.ic_folder,
+    order = 1,
+) {
   companion object {
       const val ID = "ide.main.openProject"
   }
-  ...
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/actions/main/OpenProjectAction.kt`
around lines 13 - 43, Extract a small abstract base (e.g., MainScreenAction or
MainActivityAction) that implements the common ActionItem scaffolding used by
OpenProjectAction, CreateProjectAction, and CloneRepositoryAction: move shared
properties (label, visible, enabled, icon, requiresUIThread, location, order)
and the MainActivity gating logic from prepare() into the base, provide a
protected abstract suspend fun perform(main: MainActivity): Any (or similar)
that concrete actions implement, and implement execAction() in the base to
safely cast data.get(Context::class.java) to MainActivity and call perform(),
then update OpenProjectAction (and the other two classes) to extend this base
and implement perform() instead of duplicating prepare()/execAction().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/com/itsaky/androidide/actions/main/DocsAction.kt`:
- Around line 45-47: The Intent currently sets CONTENT_TITLE_KEY to
context.getString(string.back_to_cogo) which will label the HelpActivity with
the back-navigation text; change the extra so CONTENT_TITLE_KEY uses the
docs/help title resource instead (e.g., context.getString(string.docs) or the
appropriate R.string.help label) so HelpActivity's action bar shows the page
title; update the putExtra call that references CONTENT_TITLE_KEY in the
Intent(...) block that launches HelpActivity.

In `@app/src/main/java/com/itsaky/androidide/adapters/MainActionsListAdapter.kt`:
- Around line 54-65: The button's enabled state isn't bound to action.enabled,
leaving a control that looks active but ignores clicks; update the view binding
(binding.root) to reflect action.enabled (e.g., set binding.root.isEnabled =
action.enabled and optionally binding.root.isClickable = action.enabled) so the
UI is visually and interactively disabled when action.enabled is false; make
this change near the existing setup code that assigns text, icon,
contentDescription and the listeners in MainActionsListAdapter (where
action.label, action.icon and the click/long-click handlers are configured).

In `@app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt`:
- Around line 45-53: MainFragment is taking a one-time snapshot of
ActionsRegistry.getInstance().getActions(ActionItem.Location.MAIN_SCREEN).values
which can become stale because MainActivity re-registers and clears MAIN_SCREEN
actions in onResume(); update MainFragment to refresh its action list after the
final registration point (e.g., move the getActions(...) call into
onResume()/onStart or add a registry change callback) so adapters always use the
live ActionItem instances; reference ActionsRegistry.getInstance(),
getActions(ActionItem.Location.MAIN_SCREEN), MainFragment adapter setup, and
MainActivity.onResume() when implementing the refresh or listener approach.

In `@app/src/main/java/com/itsaky/androidide/shortcuts/IdeShortcutActions.kt`:
- Around line 29-30: The code currently returns early when actionsRegistry isn't
a DefaultActionsRegistry or when findActionById(...) returns null, which
prevents the fallback path from running; instead, remove those early returns and
let the method continue to the fallback handling: treat registry as nullable
(keep "val registry = actionsRegistry as? DefaultActionsRegistry" without
returning) and if "val action = findActionById(actionsRegistry, actionId)" is
null do not return false but fall through so the existing fallback execution in
ShortcutManager.dispatch can run; reference DefaultActionsRegistry,
actionsRegistry, findActionById, actionId and the dispatch/fallback flow when
updating the control flow.
- Around line 31-33: After calling action.prepare(data) the code only checks
action.enabled which lets actions that were marked invisible during prepare()
still run; update the guard to also respect visibility by checking
action.visible (or equivalent) after prepare and return false if the action is
not visible before calling registry.executeAction(action, data). Locate the call
sequence action.prepare(data), action.enabled and registry.executeAction(action,
data) and add a visibility check (ensure you use the action.visible/isVisible
property or method used elsewhere, e.g., DonateAction.markInvisible()) so hidden
actions are not executed via shortcuts.

---

Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/actions/main/DonateAction.kt`:
- Around line 29-35: In DonateAction.prepare(ActionData) remove the dead
conditional that checks data.get(Context::class.java) for null since
markInvisible() is called unconditionally; delete the if block (the null-context
check and its markInvisible() call) so the method only calls super.prepare(data)
and the single temporary markInvisible(), keeping DonateAction and prepare as
the points to edit.

In `@app/src/main/java/com/itsaky/androidide/actions/main/OpenProjectAction.kt`:
- Around line 13-43: Extract a small abstract base (e.g., MainScreenAction or
MainActivityAction) that implements the common ActionItem scaffolding used by
OpenProjectAction, CreateProjectAction, and CloneRepositoryAction: move shared
properties (label, visible, enabled, icon, requiresUIThread, location, order)
and the MainActivity gating logic from prepare() into the base, provide a
protected abstract suspend fun perform(main: MainActivity): Any (or similar)
that concrete actions implement, and implement execAction() in the base to
safely cast data.get(Context::class.java) to MainActivity and call perform(),
then update OpenProjectAction (and the other two classes) to extend this base
and implement perform() instead of duplicating prepare()/execAction().

In `@app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt`:
- Around line 54-58: The fragment currently hard-checks registry is a
DefaultActionsRegistry in the onClick handler which prevents other
ActionsRegistry implementations from executing actions; remove the
DefaultActionsRegistry type check and invoke the execute method via the registry
interface (or inject a small ActionExecutor and call its execute(action,
actionData) from the onClick lambda). If the ActionsRegistry interface lacks an
executeAction(ActionItem, actionData) method, add that method to the interface
(or add the new ActionExecutor abstraction) and update callers to use the
interface method rather than casting to DefaultActionsRegistry; keep references
to onClick, ActionItem, registry, executeAction and actionData to locate the
change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bb52302e-048e-4763-b29a-09ed47bbdd52

📥 Commits

Reviewing files that changed from the base of the PR and between a4b67d4 and 7fe0a83.

📒 Files selected for processing (21)
  • actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt
  • app/src/main/java/com/itsaky/androidide/actions/main/CloneRepositoryAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/main/CreateProjectAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/main/DeleteProjectAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/main/DocsAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/main/DonateAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/main/OpenProjectAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/main/OpenTerminalAction.kt
  • app/src/main/java/com/itsaky/androidide/actions/main/PreferencesAction.kt
  • app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt
  • app/src/main/java/com/itsaky/androidide/adapters/MainActionsListAdapter.kt
  • app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt
  • app/src/main/java/com/itsaky/androidide/models/MainScreenAction.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/IdeShortcutActions.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/KeyShortcut.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/ShortcutManager.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ProjectsAndSolutionsGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/SearchAndReplaceGroup.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/ShortcutDefinitionIds.kt
  • app/src/main/java/com/itsaky/androidide/shortcuts/groups/WindowsAndDisplayGroup.kt
  • app/src/main/java/com/itsaky/androidide/utils/MainScreenActions.kt
💤 Files with no reviewable changes (1)
  • app/src/main/java/com/itsaky/androidide/models/MainScreenAction.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/itsaky/androidide/activities/MainActivity.kt

@hal-eisen-adfa
Copy link
Collaborator

Please divide this into 2 smaller PRs - thanks

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt (1)

108-111: Optional: extract the split index into a named constant.

Using a named constant for 3 will make the portrait/landscape action partition easier to maintain.

♻️ Suggested tweak
 class MainFragment : BaseFragment() {
+	private companion object {
+		const val LANDSCAPE_LEFT_ACTION_COUNT = 3
+	}
@@
-		val leftActions = if (binding!!.actionsRight != null) actions.take(3) else actions
+		val leftActions = if (binding!!.actionsRight != null) {
+			actions.take(LANDSCAPE_LEFT_ACTION_COUNT)
+		} else actions
 		binding!!.actions.adapter = MainActionsListAdapter(leftActions, onClick, onLongClick)
-		binding!!.actionsRight?.adapter = MainActionsListAdapter(actions.drop(3), onClick, onLongClick)
+		binding!!.actionsRight?.adapter = MainActionsListAdapter(
+			actions.drop(LANDSCAPE_LEFT_ACTION_COUNT),
+			onClick,
+			onLongClick
+		)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt` around
lines 108 - 111, The hard-coded split index "3" used to partition actions (seen
in actions.take(3) and actions.drop(3) when creating leftActions and setting
adapters for binding!!.actions and binding!!.actionsRight) should be extracted
to a named constant (e.g., ACTIONS_SPLIT_INDEX) and used in place of both
literals; update the leftActions assignment and the adapter for actionsRight to
reference ACTIONS_SPLIT_INDEX and keep MainActionsListAdapter creation unchanged
so the partition logic is centralized and easier to adjust.
app/src/main/java/com/itsaky/androidide/adapters/MainActionsListAdapter.kt (1)

63-65: Consider not consuming long-click when no handler is provided.

Defaulting to true here swallows long-press events even when the adapter has no long-click behavior.

♻️ Suggested tweak
             setOnLongClickListener {
-                onLongClick?.invoke(action, it) ?: true
+                onLongClick?.invoke(action, it) ?: false
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/itsaky/androidide/adapters/MainActionsListAdapter.kt`
around lines 63 - 65, The long-click listener currently always consumes the
event by returning true when no handler exists; change the return to not consume
the event by using the actual handler result or false when absent (replace the
Elvis default from true to false), i.e. update the setOnLongClickListener lambda
that calls onLongClick?.invoke(action, it) to return false when onLongClick is
null so long-presses bubble through.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/src/main/java/com/itsaky/androidide/adapters/MainActionsListAdapter.kt`:
- Around line 63-65: The long-click listener currently always consumes the event
by returning true when no handler exists; change the return to not consume the
event by using the actual handler result or false when absent (replace the Elvis
default from true to false), i.e. update the setOnLongClickListener lambda that
calls onLongClick?.invoke(action, it) to return false when onLongClick is null
so long-presses bubble through.

In `@app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt`:
- Around line 108-111: The hard-coded split index "3" used to partition actions
(seen in actions.take(3) and actions.drop(3) when creating leftActions and
setting adapters for binding!!.actions and binding!!.actionsRight) should be
extracted to a named constant (e.g., ACTIONS_SPLIT_INDEX) and used in place of
both literals; update the leftActions assignment and the adapter for
actionsRight to reference ACTIONS_SPLIT_INDEX and keep MainActionsListAdapter
creation unchanged so the partition logic is centralized and easier to adjust.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eaedb67e-0f6a-4ffa-85c9-85a41c1385f4

📥 Commits

Reviewing files that changed from the base of the PR and between 7fe0a83 and d31f305.

📒 Files selected for processing (2)
  • app/src/main/java/com/itsaky/androidide/adapters/MainActionsListAdapter.kt
  • app/src/main/java/com/itsaky/androidide/fragments/MainFragment.kt

@jatezzz jatezzz force-pushed the feat/ADFA-3153-ide-shortcut-system branch from d31f305 to a4b67d4 Compare March 18, 2026 20:26
@jatezzz
Copy link
Collaborator Author

jatezzz commented Mar 18, 2026

@itsaky-adfa Thanks for your comments, you were absolutely right.

As @hal-eisen-adfa requested, the PR was split into two to maintain better reviewability. The changes have been addressed in #1091.

Could you please take a look?

@jatezzz jatezzz requested a review from itsaky-adfa March 19, 2026 14:01
@jatezzz jatezzz requested a review from itsaky-adfa March 19, 2026 16:56
jatezzz and others added 5 commits March 20, 2026 08:24
… system

add grouped shortcuts and editor dispatch integration
refactor(shortcuts): rename saveFile to saveAllFiles for consistency
* fix(main): refresh main screen actions on resume
refactor(shortcuts): route main screen actions through registry and update bindings

* refactor: migrate shortcut actions logging to SLF4J and improve error visibility

Add logs for missing action data, registry mismatch, and unresolved action IDs to avoid silent failures
@jatezzz jatezzz force-pushed the feat/ADFA-3153-ide-shortcut-system branch from 3dd82c7 to 1e9747f Compare March 20, 2026 13:25
@jatezzz jatezzz merged commit 8fe7002 into stage Mar 20, 2026
2 checks passed
@jatezzz jatezzz deleted the feat/ADFA-3153-ide-shortcut-system branch March 20, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants