Skip to content

Commit daaccab

Browse files
authored
ADFA-3569 | Refactor editor fullscreen state handling (#1149)
* feat(editor): add fullscreen mode and remove immersive controller * refactor(editor): clean up fullscreen state handling * fix(editor): resolve PR feedback for bottom sheet anchor, state binding, and redundant LiveData
1 parent f57d73b commit daaccab

13 files changed

Lines changed: 410 additions & 479 deletions

File tree

app/src/main/java/com/itsaky/androidide/activities/editor/BaseEditorActivity.kt

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ abstract class BaseEditorActivity :
177177

178178
private val fileManagerViewModel by viewModels<FileManagerViewModel>()
179179
private var feedbackButtonManager: FeedbackButtonManager? = null
180-
private var immersiveController: LandscapeImmersiveController? = null
180+
private var fullscreenManager: FullscreenManager? = null
181181
private val topEdgeThreshold by lazy { SizeUtils.dp2px(TOP_EDGE_SWIPE_THRESHOLD_DP) }
182182

183183
var isDestroying = false
@@ -455,8 +455,8 @@ abstract class BaseEditorActivity :
455455
editorBottomSheet = null
456456
gestureDetector = null
457457

458-
immersiveController?.destroy()
459-
immersiveController = null
458+
fullscreenManager?.destroy()
459+
fullscreenManager = null
460460

461461
_binding = null
462462

@@ -498,7 +498,6 @@ abstract class BaseEditorActivity :
498498
}
499499

500500
private fun applyStandardInsets(systemBars: Insets) {
501-
immersiveController?.onSystemBarInsetsChanged(systemBars.top)
502501
val root = _binding?.root ?: return
503502
val initial = root.getOrStoreInitialPadding()
504503
root.updatePadding(bottom = initial.bottom + systemBars.bottom)
@@ -627,14 +626,21 @@ abstract class BaseEditorActivity :
627626
content.tabs.addOnTabSelectedListener(this)
628627

629628
setupStateObservers()
629+
setupFullscreenObserver()
630630
setupViews()
631631

632-
immersiveController = LandscapeImmersiveController(
632+
fullscreenManager = FullscreenManager(
633633
contentBinding = content,
634634
bottomSheetBehavior = editorBottomSheet!!,
635+
closeDrawerAction = {
636+
binding.editorDrawerLayout.closeDrawer(GravityCompat.START)
637+
},
638+
onFullscreenToggleRequested = {
639+
editorViewModel.toggleFullscreen()
640+
},
635641
).also {
636642
it.bind()
637-
it.onConfigurationChanged(resources.configuration)
643+
it.render(editorViewModel.isFullscreen, animate = false)
638644
}
639645

640646
setupContainers()
@@ -668,16 +674,17 @@ abstract class BaseEditorActivity :
668674

669675
override fun onConfigurationChanged(newConfig: Configuration) {
670676
super.onConfigurationChanged(newConfig)
671-
immersiveController?.onConfigurationChanged(newConfig)
672677
window?.decorView?.let { ViewCompat.requestApplyInsets(it) }
673678
reapplySystemBarInsetsFromRoot()
674679
_binding?.content?.applyBottomSheetAnchorForOrientation(newConfig.orientation)
680+
fullscreenManager?.render(editorViewModel.isFullscreen, animate = false)
675681
}
676682

677683
private fun reapplySystemBarInsetsFromRoot() {
678684
val root = _binding?.root ?: return
679685
val rootInsets = ViewCompat.getRootWindowInsets(root)
680686
if (rootInsets == null) {
687+
// Insets can be temporarily unavailable right after a configuration change.
681688
root.post { reapplySystemBarInsetsFromRoot() }
682689
return
683690
}
@@ -844,7 +851,6 @@ abstract class BaseEditorActivity :
844851
}
845852

846853
override fun onPause() {
847-
immersiveController?.onPause()
848854
super.onPause()
849855
memoryUsageWatcher.listener = null
850856
memoryUsageWatcher.stopWatching(false)
@@ -1223,6 +1229,16 @@ abstract class BaseEditorActivity :
12231229
}
12241230
}
12251231

1232+
private fun setupFullscreenObserver() {
1233+
lifecycleScope.launch {
1234+
repeatOnLifecycle(Lifecycle.State.STARTED) {
1235+
editorViewModel.uiState.collectLatest { uiState ->
1236+
fullscreenManager?.render(uiState.isFullscreen, animate = true)
1237+
}
1238+
}
1239+
}
1240+
}
1241+
12261242
private fun setupViews() {
12271243
setupNoEditorView()
12281244
setupBottomSheet()
@@ -1383,7 +1399,7 @@ abstract class BaseEditorActivity :
13831399

13841400
content.apply {
13851401
viewContainer.viewTreeObserver.addOnGlobalLayoutListener(observer)
1386-
bottomSheet.setOffsetAnchor(editorAppBarLayout)
1402+
applyBottomSheetAnchorForOrientation(resources.configuration.orientation)
13871403
}
13881404
}
13891405

@@ -1447,31 +1463,48 @@ abstract class BaseEditorActivity :
14471463
val isHorizontalSwipe = abs(diffX) > abs(diffY)
14481464

14491465
val hasDownFlingDistance = diffY > flingDistanceThreshold
1450-
val hasRightFlingDistance = diffX > flingDistanceThreshold
1466+
val hasUpFlingDistance = diffY < -flingDistanceThreshold
14511467

14521468
val hasVerticalVelocity = abs(velocityY) > flingVelocityThreshold
14531469
val hasHorizontalVelocity = abs(velocityX) > flingVelocityThreshold
1470+
val hasRightFlingDistance = diffX > flingDistanceThreshold
1471+
1472+
val screenHeight = resources.displayMetrics.heightPixels
1473+
val bottomEdgeThreshold = screenHeight - topEdgeThreshold
14541474

1455-
// Check for a swipe down (to show top bar)
1456-
// This is placed before the noFilesOpen check so it works while editing
1457-
val isLandscape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
14581475
val startedNearTopEdge = e1.y < topEdgeThreshold
1476+
val startedNearBottomEdge = e1.y > bottomEdgeThreshold
1477+
val isTopEdgeDismissFling = isVerticalSwipe &&
1478+
hasVerticalVelocity &&
1479+
startedNearTopEdge &&
1480+
hasDownFlingDistance
1481+
val isBottomEdgeDismissFling = isVerticalSwipe &&
1482+
hasVerticalVelocity &&
1483+
startedNearBottomEdge &&
1484+
hasUpFlingDistance
1485+
val isDrawerOpenFling = hasRightFlingDistance &&
1486+
hasHorizontalVelocity &&
1487+
isHorizontalSwipe
1488+
1489+
// Fullscreen mode can be dismissed with an inward fling from either vertical edge.
1490+
if (isTopEdgeDismissFling && editorViewModel.isFullscreen) {
1491+
editorViewModel.exitFullscreen()
1492+
return true
1493+
}
14591494

1460-
if (isLandscape && startedNearTopEdge && hasDownFlingDistance && hasVerticalVelocity && isVerticalSwipe) {
1461-
immersiveController?.showTopBar()
1495+
if (isBottomEdgeDismissFling && editorViewModel.isFullscreen) {
1496+
editorViewModel.exitFullscreen()
14621497
return true
14631498
}
14641499

1465-
// Check if no files are open by looking at the displayedChild of the view flipper
1500+
// Preserve the editor interaction area; drawer gestures are only enabled on the empty state.
14661501
val noFilesOpen = content.viewContainer.displayedChild == 1
14671502
if (!noFilesOpen) {
1468-
return false // If files are open, do nothing
1503+
return false
14691504
}
14701505

1471-
// Check for a right swipe (to open left drawer) - This part is still correct
1472-
// Added abs(diffX) > abs(diffY) to prevent diagonal swipes from triggering this
1473-
if (hasRightFlingDistance && hasHorizontalVelocity && isHorizontalSwipe) {
1474-
// Use the correct binding for the drawer layout
1506+
// Filter out diagonal flings so only an intentional right swipe opens the drawer.
1507+
if (isDrawerOpenFling) {
14751508
binding.editorDrawerLayout.openDrawer(GravityCompat.START)
14761509
return true
14771510
}

0 commit comments

Comments
 (0)