Skip to content

ADFA-3569 | Refactor editor fullscreen state handling#1149

Merged
jatezzz merged 3 commits intostagefrom
feat/ADFA-3569-fullscreen-portrait-and-landscape
Apr 7, 2026
Merged

ADFA-3569 | Refactor editor fullscreen state handling#1149
jatezzz merged 3 commits intostagefrom
feat/ADFA-3569-fullscreen-portrait-and-landscape

Conversation

@jatezzz
Copy link
Copy Markdown
Collaborator

@jatezzz jatezzz commented Apr 2, 2026

Description

Refactored the editor's immersive mode by replacing the orientation-specific LandscapeImmersiveController with a universal FullscreenManager. Fullscreen state is now centrally managed and observed via EditorViewModel using a StateFlow. Additionally, the disparate top and bottom bar toggle buttons were replaced with a single, unified fullscreen toggle button.

Details

Dex/Tablet

document_5075574213318805643.mp4

Phone

Screen_Recording_20260402_161348_Code.on.the.Go.mp4

Ticket

ADFA-3569

Observation

The previous implementation restricted immersive capabilities to landscape mode and relied on direct view manipulation. Moving the state to the ViewModel and utilizing a dedicated FullscreenManager creates a more predictable, reactive, and orientation-independent user experience.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough
  • Features & Improvements

    • Unified fullscreen control: replaced separate top/bottom toggle buttons with a single fullscreen toggle.
    • Orientation-independent fullscreen: removed landscape-only restriction; fullscreen works in all orientations.
    • Reactive state management: fullscreen state moved into EditorViewModel and exposed as a StateFlow (UiState.isFullscreen).
    • Centralized fullscreen manager: introduced FullscreenManager to orchestrate enter/exit rendering and UI transitions.
    • Simplified bottom sheet behavior: normalized/clamped linear scaling for header height and bottom padding.
    • Gesture updates: replaced landscape-only “swipe down to show top bar” with inward-fling dismissal from top or bottom edges; tightened drawer-opening fling condition.
  • Notable code changes

    • Replaced LandscapeImmersiveController with FullscreenManager and added continuous StateFlow-driven rendering in BaseEditorActivity.
    • EditorViewModel: added UiState, uiState: StateFlow, var isFullscreen, and control methods (updateFullscreen, toggleFullscreen, exitFullscreen).
    • New public class FullscreenManager with bind(), destroy(), render(isFullscreen, animate).
    • Layout/resource changes: removed btn_toggle_top_bar / btn_toggle_bottom_bar, added btn_fullscreen_toggle, updated editor_sheet_collapsed_height from 56dp → 100dp, added ic_fullscreen / ic_fullscreen_exit drawables, removed bg_hollow_green_circle.
    • EditorBottomSheet behavior simplified; WindowInsetsExtensions no longer branches on orientation for appbar insets and now updates btn_fullscreen_toggle margins.
    • Removed orientation-specific bottom-sheet anchoring helpers and LandscapeImmersiveController.kt (deleted).
  • Risks & best-practice violations

    • High
      • No test coverage shipped with this large refactor (no unit or UI tests), increasing regression risk for lifecycle, gestures, and layout behavior.
      • Complex transition/timing logic in FullscreenManager (token-delayed runnables) may introduce race conditions or missed updates.
      • Gesture behavior changed substantially; regressions likely across devices and orientations without broad device testing.
      • Deletion of drawable bg_hollow_green_circle may break callers not covered by this diff or by downstream themes.
    • Medium
      • StateFlow lifecycle integration must be validated to avoid missed renders or leaks (ensure observers registered/unregistered correctly around STARTED/STOPPED).
      • Bottom sheet collapsed height increased notably (56dp → 100dp); may cause visual/layout regressions across screens.
      • Breaking resource IDs: btn_toggle_top_bar / btn_toggle_bottom_bar removed—update UI tests, telemetry, and any external references.
      • New listeners/callbacks must be cleaned up correctly in destroy() to avoid memory leaks.
    • Low
      • Orientation-inset simplification may change inset behavior subtly on some device manufacturers; verify consistent insets.
      • New vector drawables should be validated across densities and themes (tinting/color attributes).
  • Recommended testing before merge

    • Full device/emulator matrix: phones and tablets in portrait and landscape (including foldables/dex where applicable).
    • UI regression testing for editor layout, bottom sheet spacing, and app bar behavior.
    • Gesture tests: fling thresholds, drawer gating, fullscreen enter/exit in various states.
    • Lifecycle tests: rotation, configuration changes, background/foreground transitions, and memory profiling to detect leaks.
    • Accessibility & automation updates: verify content descriptions (desc_enter_fullscreen / desc_exit_fullscreen) and update any tests referencing removed IDs.
    • Verify there are no remaining references to removed resources/classes (LandscapeImmersiveController, bg_hollow_green_circle) and update documentation/README if applicable.

Walkthrough

Replaces the landscape-specific immersive controller with a new FullscreenManager and StateFlow-driven fullscreen UI state in EditorViewModel; updates BaseEditorActivity wiring, gestures, insets, bottom-sheet anchoring, layouts, icons, and strings to support unified fullscreen behavior across orientations.

Changes

Cohort / File(s) Summary
Fullscreen state & controller
app/src/main/java/com/itsaky/androidide/viewmodel/EditorViewModel.kt, app/src/main/java/com/itsaky/androidide/activities/editor/FullscreenManager.kt
Added UiState + StateFlow and isFullscreen API to EditorViewModel. Introduced FullscreenManager to bind UI, manage app-bar & bottom-sheet listeners, and render fullscreen/windowed transitions with toggle/close callbacks.
Activity wiring & gestures
app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt
Replaced LandscapeImmersiveController usage with fullscreenManager, removed its lifecycle calls, added setupFullscreenObserver() to sync UI state, changed pause/config handling, and updated fling/gesture logic to exit fullscreen or open drawer under new conditions.
Removed legacy controller
app/src/main/java/com/itsaky/androidide/activities/editor/LandscapeImmersiveController.kt
Deleted the LandscapeImmersiveController class and all its binding, listeners, lifecycle hooks, and inset-handling logic.
Bottom sheet behavior
app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt
Simplified onSlide() math: removed collapse-threshold branching, clamp sheetOffset to [0,1], use linear height/padding scaling; removed related constant and piecewise logic.
Window insets & anchors
app/src/main/java/com/itsaky/androidide/utils/WindowInsetsExtensions.kt
Removed orientation-branching in applyResponsiveAppBarInsets(), switched immersive insets updates to btnFullscreenToggle, removed refresh/reset helper functions and changed applyBottomSheetAnchorForOrientation() to post and call resetOffsetAnchor()/setOffsetAnchor(...) directly.
Layout updates
app/src/main/res/layout/content_editor.xml, app/src/main/res/layout-land/content_editor.xml
Replaced two toggle buttons with a single btn_fullscreen_toggle (icon, size, margins). Switched editor container margin and bottom-sheet peekHeight to use editor_sheet_collapsed_height.
Resources
resources/src/main/res/drawable/ic_fullscreen.xml, resources/src/main/res/drawable/ic_fullscreen_exit.xml, app/src/main/res/drawable/bg_hollow_green_circle.xml, resources/src/main/res/values/dimens.xml, resources/src/main/res/values/strings.xml
Added fullscreen/exit vector icons and new strings; removed hollow green circle drawable and two old toggle description strings; increased editor_sheet_collapsed_height from 56dp → 100dp.

Sequence Diagram

sequenceDiagram
    participant User
    participant BaseEditorActivity
    participant FullscreenManager
    participant EditorViewModel
    participant AppBarLayout
    participant BottomSheetBehavior

    User->>BaseEditorActivity: onCreate()
    BaseEditorActivity->>FullscreenManager: construct + bind()
    FullscreenManager->>AppBarLayout: register OnOffsetChangedListener()
    FullscreenManager->>BottomSheetBehavior: add BottomSheetCallback()
    FullscreenManager->>BaseEditorActivity: closeDrawerAction / onFullscreenToggleRequested callbacks

    BaseEditorActivity->>EditorViewModel: setupFullscreenObserver() (STARTED)
    EditorViewModel-->>BaseEditorActivity: uiState.isFullscreen updates

    User->>FullscreenManager: tap fullscreen toggle
    FullscreenManager->>EditorViewModel: onFullscreenToggleRequested()
    EditorViewModel->>EditorViewModel: toggleFullscreen()
    EditorViewModel-->>BaseEditorActivity: emit new uiState

    BaseEditorActivity->>FullscreenManager: render(isFullscreen, animate)
    FullscreenManager->>AppBarLayout: expand/collapse & animate alpha
    FullscreenManager->>BottomSheetBehavior: set hideable/state & adjust margin
    FullscreenManager->>BaseEditorActivity: close drawer (if needed)

    User->>BaseEditorActivity: fling from edge (dismiss)
    BaseEditorActivity->>EditorViewModel: exitFullscreen()
    EditorViewModel-->>FullscreenManager: render(isFullscreen=false, animate=true)

    BaseEditorActivity->>FullscreenManager: preDestroy()/destroy()
    FullscreenManager->>AppBarLayout: unregister listener
    FullscreenManager->>BottomSheetBehavior: remove callback
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • itsaky-adfa
  • Daniel-ADFA
  • dara-abijo-adfa

Poem

🐰
I hopped in code to chase the bar,
Replaced the old with fullscreen star.
With toggles bright and gestures fleet,
The editor now feels tidy & neat.
StateFlows hum — the view’s complete. 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.50% 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 'ADFA-3569 | Refactor editor fullscreen state handling' directly and clearly summarizes the main change: refactoring fullscreen state handling in the editor.
Description check ✅ Passed The description is directly related to the changeset, explaining the replacement of LandscapeImmersiveController with FullscreenManager, state management via EditorViewModel, and UI unification.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ADFA-3569-fullscreen-portrait-and-landscape

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
Copy Markdown
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/viewmodel/EditorViewModel.kt (1)

62-67: Consider consolidating to a single state source.

The fullscreen state is maintained in both _isFullscreen (LiveData) and _uiState (StateFlow). While updateFullscreen keeps them synchronized, this dual-source pattern increases maintenance overhead and risk of divergence.

If the LiveData is still needed for legacy observers, consider deriving it from the StateFlow or document why both are required. Otherwise, consolidating to StateFlow alone would simplify the state management.

🤖 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/viewmodel/EditorViewModel.kt` around
lines 62 - 67, Consolidate fullscreen state into the single source _uiState:
remove the backing MutableLiveData _isFullscreen and have updateFullscreen
update only _uiState (UiState.fullscreen flag); if legacy LiveData observers are
required, expose a derived LiveData by converting _uiState (e.g., via asLiveData
or a mapping) so observers read from that derived LiveData instead of a separate
_isFullscreen, ensuring updateFullscreen no longer needs to sync two sources and
eliminating divergence risk.
app/src/main/java/com/itsaky/androidide/utils/WindowInsetsExtensions.kt (1)

109-113: Unused parameter orientation should be removed.

The orientation parameter is no longer used after simplifying to always call resetOffsetAnchor(). This creates a misleading API signature.

♻️ Proposed fix to remove unused parameter
-fun ContentEditorBinding.applyBottomSheetAnchorForOrientation(orientation: Int) {
+fun ContentEditorBinding.applyBottomSheetAnchor() {
     bottomSheet.resetOffsetAnchor()
 }

Note: This would also require updating the call sites in BaseEditorActivity.kt (lines 679 and 1402).

🤖 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/WindowInsetsExtensions.kt`
around lines 109 - 113, Remove the unused orientation parameter from the
function signature of ContentEditorBinding.applyBottomSheetAnchorForOrientation
and update all call sites to the new zero-arg function; specifically, change the
method to take no parameters and keep the body as
bottomSheet.resetOffsetAnchor(), then update callers (e.g., in
BaseEditorActivity where applyBottomSheetAnchorForOrientation(...) is invoked)
to call applyBottomSheetAnchorForOrientation() with no argument. Ensure method
references/imports/overloads are adjusted accordingly so there are no
compilation errors.
🤖 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/utils/WindowInsetsExtensions.kt`:
- Around line 109-113: Remove the unused orientation parameter from the function
signature of ContentEditorBinding.applyBottomSheetAnchorForOrientation and
update all call sites to the new zero-arg function; specifically, change the
method to take no parameters and keep the body as
bottomSheet.resetOffsetAnchor(), then update callers (e.g., in
BaseEditorActivity where applyBottomSheetAnchorForOrientation(...) is invoked)
to call applyBottomSheetAnchorForOrientation() with no argument. Ensure method
references/imports/overloads are adjusted accordingly so there are no
compilation errors.

In `@app/src/main/java/com/itsaky/androidide/viewmodel/EditorViewModel.kt`:
- Around line 62-67: Consolidate fullscreen state into the single source
_uiState: remove the backing MutableLiveData _isFullscreen and have
updateFullscreen update only _uiState (UiState.fullscreen flag); if legacy
LiveData observers are required, expose a derived LiveData by converting
_uiState (e.g., via asLiveData or a mapping) so observers read from that derived
LiveData instead of a separate _isFullscreen, ensuring updateFullscreen no
longer needs to sync two sources and eliminating divergence risk.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ecb6ff41-e949-4b60-9a4a-162f92e75e9c

📥 Commits

Reviewing files that changed from the base of the PR and between cb05a7d and 23ff5b6.

📒 Files selected for processing (13)
  • app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/FullscreenManager.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/LandscapeImmersiveController.kt
  • app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt
  • app/src/main/java/com/itsaky/androidide/utils/WindowInsetsExtensions.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/EditorViewModel.kt
  • app/src/main/res/drawable/bg_hollow_green_circle.xml
  • app/src/main/res/layout-land/content_editor.xml
  • app/src/main/res/layout/content_editor.xml
  • resources/src/main/res/drawable/ic_fullscreen.xml
  • resources/src/main/res/drawable/ic_fullscreen_exit.xml
  • resources/src/main/res/values/dimens.xml
  • resources/src/main/res/values/strings.xml
💤 Files with no reviewable changes (2)
  • app/src/main/res/drawable/bg_hollow_green_circle.xml
  • app/src/main/java/com/itsaky/androidide/activities/editor/LandscapeImmersiveController.kt

Copy link
Copy Markdown
Contributor

@itsaky-adfa itsaky-adfa left a comment

Choose a reason for hiding this comment

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

When the bottom sheet is expanded, we used to have the toolbar visible and the bottom sheet anchored below it. That behavior has been removed and the bottom sheet now spans the full screen instead. Is that intentional?

@jatezzz jatezzz force-pushed the feat/ADFA-3569-fullscreen-portrait-and-landscape branch from 23ff5b6 to 5a8bcdb Compare April 7, 2026 14:35
@jatezzz
Copy link
Copy Markdown
Collaborator Author

jatezzz commented Apr 7, 2026

@itsaky-adfa

When the bottom sheet is expanded, we used to have the toolbar visible and the bottom sheet anchored below it. That behavior has been removed and the bottom sheet now spans the full screen instead. Is that intentional?

Great catch! That was an oversight during the refactor, but it led to a nice UX improvement.

I've updated the logic to be orientation-aware: in portrait mode, the bottom sheet is anchored again so the toolbar remains visible, just like before. In landscape, it now removes the anchor to maximize the limited vertical screen real estate.

@jatezzz jatezzz requested a review from itsaky-adfa April 7, 2026 14:37
Copy link
Copy Markdown
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 (1)
app/src/main/java/com/itsaky/androidide/activities/editor/FullscreenManager.kt (1)

228-241: Consider setting isHideable = false after bottom sheet reaches collapsed state.

When exiting fullscreen, if the bottom sheet is already collapsed, isHideable is set to false immediately. However, if the state transition is animated, the sheet might not be collapsed yet when this code runs. The bottom sheet callback at Line 189-191 handles this case, but the logic split between two locations could be confusing.

💡 Optional: Consolidate hideable management in the callback

Consider moving all isHideable = false logic to handleBottomSheetStateChange for consistency:

 private fun applyNonFullscreen(animate: Boolean) {
     topBar.setExpanded(true, animate)
     appBarContent.alpha = 1f

     if (bottomSheetBehavior.state != BottomSheetBehavior.STATE_COLLAPSED) {
         bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
-    } else {
-        bottomSheetBehavior.isHideable = false
     }
+    // Note: isHideable is set to false in handleBottomSheetStateChange when collapsed

     editorContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> {
         bottomMargin = defaultEditorBottomMargin
     }
 }
🤖 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/FullscreenManager.kt`
around lines 228 - 241, The current applyNonFullscreen(animate: Boolean) sets
bottomSheetBehavior.isHideable = false immediately when the sheet is already
collapsed, but when the collapse is animated the sheet may not yet be collapsed;
instead consolidate hideable management into the BottomSheet callback
(handleBottomSheetStateChange) so isHideable is set only once the state reaches
BottomSheetBehavior.STATE_COLLAPSED, and in applyNonFullscreen only set
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED (never change
isHideable); if you want to preserve the existing fast-path for
already-collapsed state, check bottomSheetBehavior.state ==
BottomSheetBehavior.STATE_COLLAPSED and then set isHideable = false, otherwise
rely on handleBottomSheetStateChange to set isHideable when the collapsed state
is reached.
🤖 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/activities/editor/FullscreenManager.kt`:
- Around line 228-241: The current applyNonFullscreen(animate: Boolean) sets
bottomSheetBehavior.isHideable = false immediately when the sheet is already
collapsed, but when the collapse is animated the sheet may not yet be collapsed;
instead consolidate hideable management into the BottomSheet callback
(handleBottomSheetStateChange) so isHideable is set only once the state reaches
BottomSheetBehavior.STATE_COLLAPSED, and in applyNonFullscreen only set
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED (never change
isHideable); if you want to preserve the existing fast-path for
already-collapsed state, check bottomSheetBehavior.state ==
BottomSheetBehavior.STATE_COLLAPSED and then set isHideable = false, otherwise
rely on handleBottomSheetStateChange to set isHideable when the collapsed state
is reached.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9f352448-7ea0-4be8-b403-1896a10c31d1

📥 Commits

Reviewing files that changed from the base of the PR and between 23ff5b6 and 5a8bcdb.

📒 Files selected for processing (13)
  • app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/FullscreenManager.kt
  • app/src/main/java/com/itsaky/androidide/activities/editor/LandscapeImmersiveController.kt
  • app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt
  • app/src/main/java/com/itsaky/androidide/utils/WindowInsetsExtensions.kt
  • app/src/main/java/com/itsaky/androidide/viewmodel/EditorViewModel.kt
  • app/src/main/res/drawable/bg_hollow_green_circle.xml
  • app/src/main/res/layout-land/content_editor.xml
  • app/src/main/res/layout/content_editor.xml
  • resources/src/main/res/drawable/ic_fullscreen.xml
  • resources/src/main/res/drawable/ic_fullscreen_exit.xml
  • resources/src/main/res/values/dimens.xml
  • resources/src/main/res/values/strings.xml
💤 Files with no reviewable changes (2)
  • app/src/main/res/drawable/bg_hollow_green_circle.xml
  • app/src/main/java/com/itsaky/androidide/activities/editor/LandscapeImmersiveController.kt
✅ Files skipped from review due to trivial changes (3)
  • resources/src/main/res/values/dimens.xml
  • resources/src/main/res/drawable/ic_fullscreen.xml
  • resources/src/main/res/drawable/ic_fullscreen_exit.xml
🚧 Files skipped from review as they are similar to previous changes (2)
  • resources/src/main/res/values/strings.xml
  • app/src/main/java/com/itsaky/androidide/utils/WindowInsetsExtensions.kt

@jatezzz jatezzz merged commit daaccab into stage Apr 7, 2026
2 checks passed
@jatezzz jatezzz deleted the feat/ADFA-3569-fullscreen-portrait-and-landscape branch April 7, 2026 16:20
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