ADFA-4399: Floating windows feature#1456
Conversation
…on (#1449) feat(ADFA-4400): add floating-window module with docking model and overlay foundation
* feat(ADFA-4400): add floating-window module with docking model and overlay foundation * feat(ADFA-4401): add floating window chrome * ADFA-4401: localize floating window chrome accessibility labels
* feat(ADFA-4400): add floating-window module with docking model and overlay foundation * feat(ADFA-4401): add floating window chrome * feat(ADFA-4402): add per-window controller and foreground service
* feat(ADFA-4400): add floating-window module with docking model and overlay foundation * feat(ADFA-4401): add floating window chrome * feat(ADFA-4402): add per-window controller and foreground service * feat(ADFA-4403): undock and redock editor and plugin tabs
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a floating-window module, overlay runtime, tab adapters, and editor activity wiring that let file and plugin tabs be undocked into a foreground service and later redocked or closed through docking events. ChangesFloating window docking
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Suggested reviewers
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@app/src/main/java/com/itsaky/androidide/editor/floating/EditorPanelDockableContent.kt`:
- Around line 75-78: The teardown currently only happens in release(), but the
floating-window lifecycle expects EditorPanelDockableContent to clean up in
onDestroyView(). Add the DockableContent.onDestroyView() implementation to
invoke the same CodeEditorView.close() and null-out logic used by release(), so
the editor is always released even when the overlay is destroyed outside the
redock flow.
In
`@app/src/main/java/com/itsaky/androidide/editor/floating/IdeFloatingTabController.kt`:
- Around line 65-70: In IdeFloatingTabController’s floating-tab flow, the
original tab is being removed too early, before the floating copy is confirmed
to exist. Reorder the logic so remove() is only called after
DockingManager.undock and FloatingTabService.ensureRunning complete
successfully, or gate it behind a successful floating-host creation path, so the
docked tab can be restored if the floating setup fails.
- Around line 44-47: The undock flow in IdeFloatingTabController.launch
currently proceeds to mark the panel saved and close the file unconditionally
after panel.save(). Update this path so the save result is checked first; if
panel.save() returns false, stop the coroutine flow and do not call
panel.markAsSaved() or activity.closeFile(...). Keep the existing save/close
sequence only when the save succeeds.
- Around line 29-33: The event collection in IdeFloatingTabController.start is
tied to activity.lifecycleScope, so DockingManager.events can be missed when the
activity is recreated or inactive. Move the collection out of the activity
lifecycle (for example, use an application-wide scope) or change
DockingManager.events to a MutableSharedFlow with replay = 1 so new subscribers
still receive the latest Redock/Close event. Keep the fix centered around
start() and DockingManager.events.
In
`@app/src/main/java/com/itsaky/androidide/editor/floating/PluginTabDockableContent.kt`:
- Around line 37-39: `PluginTabDockableContent.createContent` currently returns
`overlayHost.view` even when `PluginEditorTabManager.newTabFragment(tabId)` is
null, which bypasses the existing failure UI. Update the flow so the
`overlayHost` only returns its normal view after `setFragment` succeeds, and
otherwise switch to the fallback error view already implemented for load
failures. Keep the null-handling logic centered around `newTabFragment(tabId)`
and `overlayHost` so a missing plugin fragment is treated as an actual failure.
In `@floating-window/src/main/AndroidManifest.xml`:
- Around line 6-8: Add the missing foreground-service declaration for
FloatingTabService in AndroidManifest.xml: the service that calls
startForeground/startForegroundService needs
android:foregroundServiceType="specialUse" on the <service> entry. Also add the
matching android.permission.FOREGROUND_SERVICE_SPECIAL_USE uses-permission
alongside the other manifest permissions. Update the existing FloatingTabService
declaration rather than introducing a new service entry.
In
`@floating-window/src/main/java/com/itsaky/androidide/floating/model/DockingManager.kt`:
- Around line 28-29: The event flow in `DockingManager.dock()` and
`DockingManager.close()` can desynchronize `_windows` from the UI because state
is removed before `MutableSharedFlow.tryEmit()` is confirmed. Update these
methods so the removal only happens after a successful emission, or handle the
`false` return from `_events.tryEmit(...)` by aborting the mutation and
preserving the tab in `_windows`. If the caller can suspend, prefer using a
coroutine-backed `emit` path; otherwise ensure `DockingEvent` delivery is
guaranteed before mutating state.
In
`@floating-window/src/main/java/com/itsaky/androidide/floating/service/FloatingTabService.kt`:
- Around line 89-97: The undock recovery in FloatingTabService is marking a tab
as floating before FloatingWindow.show() has actually succeeded, and the failure
path closes the tab instead of restoring it. Update the attach flow so
FloatingWindow.show() reports success/failure, only store the window in windows
after a successful attach, and on exceptions redock the tab through
DockingManager rather than calling DockingManager.close; use FloatingTabService,
FloatingWindow.show(), and the existing windows/failed handling to keep the tab
recoverable.
In
`@floating-window/src/main/java/com/itsaky/androidide/floating/ui/FloatingWindowChrome.kt`:
- Around line 251-261: The `combinedClickable` click handler in
`FloatingWindowChrome.kt` is swallowing all failures from
`DockAction.onInvoke()` by using `runCatching`, which also hides cancellation
and unexpected errors. Replace the broad catch with narrow handling for only the
expected failure path around the `action.onInvoke()` call, and let other
exceptions propagate or be logged. Keep the existing confirm/`ACTION_CONFIRM_MS`
behavior intact in the `onClick` coroutine.
- Around line 112-116: The resize grip in FloatingWindowChrome is still shown
even when the window is maximized, but FloatingWindow.resizeBy() does nothing
outside WindowMode.NORMAL, so hide this dead UI in non-normal modes. Update the
ResizeHandle rendering in FloatingWindowChrome to be conditional on the current
window mode, using the same state or mode source already available to the
composable, so the handle is only visible when resizing is actually supported.
In
`@floating-window/src/main/java/com/itsaky/androidide/floating/window/FloatingWindow.kt`:
- Around line 98-104: The show() method in FloatingWindow currently swallows
addView() failures via runCatching, which prevents
FloatingTabService.reconcile() from detecting and recovering from a failed show.
Remove the exception suppression in show(), keep the existing
host.attach(rootView) and windowManager.addView(rootView, params) flow, and let
addView() errors propagate so the service can retry or close the window; keep
the added flag update only on success.
In
`@floating-window/src/main/java/com/itsaky/androidide/floating/window/InitialBounds.kt`:
- Around line 22-27: The cascaded window position logic in
InitialBounds.calculate/WindowBounds only clamps to zero, so later cascade steps
can place windows beyond the visible viewport. Update the x and y calculations
to clamp within both bounds: keep the current centered base plus cascade step,
but also cap each coordinate at the maximum visible position derived from
metrics.widthPixels/heightPixels minus the window size before returning
WindowBounds, so OverlayLayoutParams.create receives on-screen coordinates.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 395f6d7f-d1d9-4b3e-8438-cc1c54f34def
📒 Files selected for processing (26)
app/build.gradle.ktsapp/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.ktapp/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.ktapp/src/main/java/com/itsaky/androidide/editor/floating/EditorPanelDockableContent.ktapp/src/main/java/com/itsaky/androidide/editor/floating/IdeFloatingTabController.ktapp/src/main/java/com/itsaky/androidide/editor/floating/PluginTabDockableContent.ktfloating-window/build.gradle.ktsfloating-window/src/main/AndroidManifest.xmlfloating-window/src/main/java/com/itsaky/androidide/floating/fragment/OverlayFragmentHost.ktfloating-window/src/main/java/com/itsaky/androidide/floating/model/DockAction.ktfloating-window/src/main/java/com/itsaky/androidide/floating/model/DockableContent.ktfloating-window/src/main/java/com/itsaky/androidide/floating/model/DockingEvent.ktfloating-window/src/main/java/com/itsaky/androidide/floating/model/DockingManager.ktfloating-window/src/main/java/com/itsaky/androidide/floating/model/FloatingTab.ktfloating-window/src/main/java/com/itsaky/androidide/floating/permission/OverlayPermission.ktfloating-window/src/main/java/com/itsaky/androidide/floating/service/FloatingTabService.ktfloating-window/src/main/java/com/itsaky/androidide/floating/ui/FloatingTheme.ktfloating-window/src/main/java/com/itsaky/androidide/floating/ui/FloatingWindowChrome.ktfloating-window/src/main/java/com/itsaky/androidide/floating/window/FloatingWindow.ktfloating-window/src/main/java/com/itsaky/androidide/floating/window/FloatingWindowHost.ktfloating-window/src/main/java/com/itsaky/androidide/floating/window/FloatingWindowState.ktfloating-window/src/main/java/com/itsaky/androidide/floating/window/InitialBounds.ktfloating-window/src/main/java/com/itsaky/androidide/floating/window/OverlayLayoutParams.ktplugin-manager/src/main/kotlin/com/itsaky/androidide/plugins/manager/ui/PluginEditorTabManager.ktresources/src/main/res/values/strings.xmlsettings.gradle.kts
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/FileOpenExtension.kt (1)
19-27: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy liftPreserve the existing
FileTabMenuItemABI. AddingtooltipTagto the primary constructor changes the generated constructor,copy(...), andcomponentN()signatures for this public data class. Independently compiled plugins can fail with linkage errors unless they’re rebuilt, so keep the old constructor shape compatible or make this an explicit plugin-API version bump.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/FileOpenExtension.kt` around lines 19 - 27, The public FileTabMenuItem data class has an ABI-breaking primary constructor change because tooltipTag was added, which alters the generated constructor, copy, and componentN signatures. Update FileTabMenuItem in FileOpenExtension.kt to preserve the existing constructor shape for binary compatibility, or move tooltipTag behind a compatibility-safe API change; if the new parameter must stay, make this an explicit plugin-api version bump and keep the old entry points available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/main/java/com/itsaky/androidide/api/IDEApiFacade.kt`:
- Around line 100-114: The one-time build listener remains registered even when
IDEApiFacade’s action launch fails synchronously, so it can later try to resume
an already-completed continuation. Fix this by either moving the listener
registration in IDEApiFacade.executeAction flow until after the null-registry
and registry.executeAction failure paths are cleared, or by explicitly
unregistering the listener before each early ToolResult.failure resume in the
registry == null branch and the catch block.
In
`@app/src/main/java/com/itsaky/androidide/editor/floating/ChromeControlTooltips.kt`:
- Around line 18-24: The tagFor(ChromeControl) mapping assigns
ChromeControl.CLOSE to TooltipTag.WINDOW_UNDOCK, which shows the wrong tooltip
for the close button. Update the ChromeControl.CLOSE branch in
ChromeControlTooltips.tagFor to use a close-specific tooltip tag if one exists,
or return null instead of reusing the undock copy. Keep the existing mappings
for MINIMIZE, MAXIMIZE, and DOCK unchanged.
---
Outside diff comments:
In
`@plugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/FileOpenExtension.kt`:
- Around line 19-27: The public FileTabMenuItem data class has an ABI-breaking
primary constructor change because tooltipTag was added, which alters the
generated constructor, copy, and componentN signatures. Update FileTabMenuItem
in FileOpenExtension.kt to preserve the existing constructor shape for binary
compatibility, or move tooltipTag behind a compatibility-safe API change; if the
new parameter must stay, make this an explicit plugin-api version bump and keep
the old entry points available.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2a082996-051b-4c36-b56c-30d30f53faab
📒 Files selected for processing (15)
app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.ktapp/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.ktapp/src/main/java/com/itsaky/androidide/api/ActionContextProvider.ktapp/src/main/java/com/itsaky/androidide/api/IDEApiFacade.ktapp/src/main/java/com/itsaky/androidide/editor/floating/ChromeControlTooltips.ktapp/src/main/java/com/itsaky/androidide/editor/floating/EditorPanelDockableContent.ktapp/src/main/java/com/itsaky/androidide/editor/floating/PluginTabDockableContent.ktapp/src/main/java/com/itsaky/androidide/utils/ActionMenuUtils.ktfloating-window/src/main/java/com/itsaky/androidide/floating/model/ChromeControl.ktfloating-window/src/main/java/com/itsaky/androidide/floating/model/DockAction.ktfloating-window/src/main/java/com/itsaky/androidide/floating/model/DockableContent.ktfloating-window/src/main/java/com/itsaky/androidide/floating/ui/FloatingWindowChrome.ktfloating-window/src/main/java/com/itsaky/androidide/floating/window/FloatingWindow.ktidetooltips/src/main/java/com/itsaky/androidide/idetooltips/TooltipTag.ktplugin-api/src/main/kotlin/com/itsaky/androidide/plugins/extensions/FileOpenExtension.kt
✅ Files skipped from review due to trivial changes (1)
- floating-window/src/main/java/com/itsaky/androidide/floating/model/ChromeControl.kt
🚧 Files skipped from review as they are similar to previous changes (5)
- floating-window/src/main/java/com/itsaky/androidide/floating/model/DockAction.kt
- app/src/main/java/com/itsaky/androidide/editor/floating/PluginTabDockableContent.kt
- app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt
- app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt
- floating-window/src/main/java/com/itsaky/androidide/floating/window/FloatingWindow.kt
…ing-window error string - EditorHandlerActivity.undockPluginTab: use the already-imported PluginEditorTabManager short name instead of the fully-qualified name. - PluginTabDockableContent: move the hardcoded error message into strings.xml (msg_floating_window_open_failed) and resolve it via context.getString().
The compose-preview module was removed from stage in a separate PR. The merge into this branch kept the now-dangling implementation(projects.composePreview) in app/build.gradle.kts, causing 'Unresolved reference: composePreview' at script compilation. No source references the compose.preview package anymore, so removing the dependency is sufficient.
No description provided.