Skip to content

ADFA-1383 - debugger toolbar tooltips#854

Merged
dara-abijo-adfa merged 6 commits intostagefrom
ADFA-1383-debugger-toolbar-tooltips
Jan 19, 2026
Merged

ADFA-1383 - debugger toolbar tooltips#854
dara-abijo-adfa merged 6 commits intostagefrom
ADFA-1383-debugger-toolbar-tooltips

Conversation

@dara-abijo-adfa
Copy link
Contributor

Show tooltips for debugger actions

@dara-abijo-adfa dara-abijo-adfa self-assigned this Jan 19, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 19, 2026

📝 Walkthrough
  • Features Added

    • Added tooltip support for debugger toolbar actions: Kill, Restart, Step Into, Step Out, Step Over, Pause/Resume.
    • Long-press now shows tooltips for toolbar items and the drag handle (Move).
    • Added 7 new TooltipTag constants for debugger actions.
    • Actions exposed tooltip metadata via new override var tooltipTag in six debugger action classes.
  • Changes

    • DebugOverlayManager: made drag handle long-clickable, adjusted touch handling (ACTION_DOWN now returns false), and shows a tooltip for Move on long-press.
    • DebuggerActionsOverlayAdapter: click execution guarded by action.enabled; added long-click handler to show tooltips anchored to the view; visual enabled state still reflected via alpha.
    • ToolTipManager: adjusted canShowPopup lifecycle checks to treat null Activity as valid, created AlertDialog via builder.create() and conditionally set window type to TYPE_APPLICATION_OVERLAY for non-Activity contexts when overlay permission is available; dialogs are shown explicitly.
    • Added an overlay permission check path to allow tooltip/dialog display outside Activity contexts.
    • Minor refactors: direct queries of action enabled state (commit-level change).
  • Risks & Best Practice Considerations

    • ⚠️ Touch handling: Changing ACTION_DOWN to return false may alter touch event propagation and parent interception behavior—verify drag, tap, and long-press interactions across devices and parent view hierarchies.
    • ⚠️ Overlay dialogs: Using TYPE_APPLICATION_OVERLAY requires SYSTEM_ALERT_WINDOW permission; ensure the app requests/handles overlay permission and test behavior on Android versions with different overlay policies.
    • ⚠️ Lifecycle checks: Treating null Activity as lifecycle-valid may surface tooltips/dialogs with unexpected contexts—confirm dialogs anchor/attach correctly and do not leak windows or cause crashes when Activity state is ambiguous.
    • ⚠️ Behavioral change: Clicks now only run when action.enabled; this is safer but may change prior UX if callers relied on clicks firing when disabled.
    • Security/privacy: Displaying overlays from non-Activity contexts can be misused; ensure tooltip content is non-sensitive and overlay usage is minimized.
  • Notes for reviewers

    • Verify tooltip texts and accessibility (contentDescription) remain correct for hover/long-press and are accessible to screen readers.
    • Test the overlay/dialog behavior with and without overlay permission on API levels that differ in overlay/window type handling.

Walkthrough

This PR adds tooltip metadata for debugger actions, enables long-press tooltip display in the debug overlay/adapters, and makes TooltipManager more robust for non-Activity contexts and overlay dialogs.

Changes

Cohort / File(s) Summary
Debugger Action Tooltip Tags
app/src/main/java/com/itsaky/androidide/actions/debug/{KillVmAction, RestartVmAction, StepIntoAction, StepOutAction, StepOverAction, SuspendResumeVmAction}.kt
Added override var tooltipTag to each action initialized to the corresponding TooltipTag constant; added TooltipTag imports.
Tooltip Tag Constants
idetooltips/src/main/java/com/itsaky/androidide/idetooltips/TooltipTag.kt
Added debugger-related constants: DEBUGGER_ACTION_MOVE, DEBUGGER_ACTION_PAUSE_RESUME, DEBUGGER_ACTION_STEP_OVER, DEBUGGER_ACTION_STEP_INTO, DEBUGGER_ACTION_STEP_OUT, DEBUGGER_ACTION_KILL, DEBUGGER_ACTION_RESTART.
Overlay Touch & Tooltip Handling
app/src/main/java/com/itsaky/androidide/services/debug/DebugOverlayManager.kt
Made drag handle long-clickable, adjusted touch ACTION_DOWN/ACTION_MOVE handling, and added long-click to show tooltip via TooltipManager with tag DEBUGGER_ACTION_MOVE.
Adapter Click/Long-Click Behavior
app/src/main/java/com/itsaky/androidide/services/debug/DebuggerActionsOverlayAdapter.kt
Guarded click execution on action enabled state, added long-click listener to show tooltip via TooltipManager, preserved tooltip text for hover.
Tooltip Manager Dialog & Lifecycle Handling
idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt
Relaxed lifecycle check (treat null Activity as valid), switched to builder.create()/dialog.show(), and set window type to TYPE_APPLICATION_OVERLAY when context isn't an Activity and overlays are allowed.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Overlay as DebugOverlay (Adapter/View)
    participant TooltipMgr as TooltipManager
    participant System as WindowManager/Dialog

    User->>Overlay: long-press on drag handle / action view
    Overlay->>TooltipMgr: request showTooltip(view, tooltipTag)
    TooltipMgr->>TooltipMgr: canShowPopup(context, view) checks lifecycle & attachment
    alt context is non-Activity and canDrawOverlays == true
        TooltipMgr->>System: create dialog with TYPE_APPLICATION_OVERLAY
    else
        TooltipMgr->>System: create dialog with default window
    end
    TooltipMgr->>System: dialog.show()
    System->>User: tooltip/dialog visible
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • Daniel-ADFA
  • itsaky-adfa
  • jomen-adfa

Poem

🐰 I hopped and nudged a tooltip bright,
Long-press wakes the debugger light.
Steps and moves now whisper low,
Tiny hints to help us grow. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% 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: adding tooltip support for debugger toolbar actions.
Description check ✅ Passed The description is directly related to the changeset, describing the core feature of showing tooltips for debugger actions.

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

✨ Finishing touches
  • 📝 Generate docstrings

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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt (1)

393-413: Add permission check before setting TYPE_APPLICATION_OVERLAY on the dialog window.

The SYSTEM_ALERT_WINDOW permission is declared in the manifest and the app has permission-checking utilities (canDrawOverlays(context) in PermissionsHelper.kt), but the code at line 411 sets TYPE_APPLICATION_OVERLAY without validating that the permission is granted. This check is necessary because the permission requires explicit runtime grant on Android 6.0+. Without it, the dialog may fail to display or throw an exception. Add a check like:

if (context !is Activity && canDrawOverlays(context)) {
    dialog.window?.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
}
🧹 Nitpick comments (1)
app/src/main/java/com/itsaky/androidide/services/debug/DebuggerActionsOverlayAdapter.kt (1)

44-52: Captured isEnabled may become stale if action state changes.

The isEnabled value is captured at bind time (line 44), but if the action's enabled state changes after binding (e.g., when prepare is called elsewhere), the click listener will use the stale value. Consider checking action.enabled directly in the click listener.

Also, line 45 uses action.enabled directly while line 50 uses the captured isEnabled - this inconsistency may cause confusion.

♻️ Suggested fix
-        val isEnabled = action.enabled
         binding.root.alpha = if (action.enabled) 1f else 0.5f
         TooltipCompat.setTooltipText(binding.root, action.label)

         binding.root.apply {
             setOnClickListener {
-                if (isEnabled) {
+                if (action.enabled) {
                     actionsRegister.executeAction(action, data)
                 }
             }

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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
idetooltips/src/main/java/com/itsaky/androidide/idetooltips/ToolTipManager.kt (1)

394-413: Guard debug dialog when there's no Activity and no overlay permission.

Line 413 can throw BadTokenException by calling dialog.show() with a non-Activity context that lacks overlay permission. The condition at line 409 should short-circuit and return early if both conditions fail (non-Activity context AND canDrawOverlays(context) is false).

The proposed fix is mechanically sound. However, note that the Activity-lookup logic already exists in the canShowPopup function (lines 220–226); consider extracting it to a helper rather than duplicating it.

🛠️ Proposed fix
+        tailrec fun Context.findActivity(): Activity? = when (this) {
+            is Activity -> this
+            is ContextWrapper -> baseContext?.findActivity()
+            else -> null
+        }
+
+        val activity = context.findActivity()
         val dialog = builder.create()
 
-        if (context !is Activity && canDrawOverlays(context)) {
-            dialog.window?.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
-        }
+        if (activity == null) {
+            if (canDrawOverlays(context)) {
+                dialog.window?.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
+            } else {
+                Log.w(TAG, "Cannot show tooltip debug dialog without overlay permission")
+                return
+            }
+        }
 
         dialog.show()

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.

2 participants