Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.collection.MutableIntIntMap
import androidx.core.graphics.Insets
import androidx.core.view.GravityCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import com.itsaky.androidide.utils.applyBottomWindowInsetsPadding
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
Expand Down Expand Up @@ -118,8 +118,13 @@ import com.itsaky.androidide.utils.FlashType
import com.itsaky.androidide.utils.InstallationResultHandler.onResult
import com.itsaky.androidide.utils.IntentUtils
import com.itsaky.androidide.utils.MemoryUsageWatcher
import com.itsaky.androidide.utils.applyResponsiveAppBarInsets
import com.itsaky.androidide.utils.applyImmersiveModeInsets
import com.itsaky.androidide.utils.applyRootSystemInsetsAsPadding
import com.itsaky.androidide.utils.applyBottomSheetAnchorForOrientation
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.flashMessage
import com.itsaky.androidide.utils.getOrStoreInitialPadding
import com.itsaky.androidide.utils.isAtLeastR
import com.itsaky.androidide.utils.resolveAttr
import com.itsaky.androidide.viewmodel.ApkInstallationViewModel
Expand Down Expand Up @@ -374,7 +379,6 @@ abstract class BaseEditorActivity :
private val flingVelocityThreshold by lazy { SizeUtils.dp2px(100f) }

private var editorAppBarInsetTop: Int = 0
private var sidebarLastInsetTop: Int = 0

companion object {
private const val TAG = "ResizePanelDebugger"
Expand Down Expand Up @@ -484,47 +488,22 @@ abstract class BaseEditorActivity :
val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime())
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())

applyStandardInsets(systemBars, insets)
applyStandardInsets(systemBars)

applyImmersiveModeInsets(systemBars)

handleKeyboardInsets(imeInsets)
}

private fun applyStandardInsets(systemBars: Insets, windowInsets: WindowInsetsCompat) {
val content = _binding?.content ?: return
val isLandscape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE

if (isLandscape) {
content.editorAppBarLayout.updatePadding(top = 0)
content.editorAppbarContent.updatePadding(top = systemBars.top)
} else {
content.editorAppBarLayout.updatePadding(top = systemBars.top)
content.editorAppbarContent.updatePadding(top = 0)
}

private fun applyStandardInsets(systemBars: Insets) {
immersiveController?.onSystemBarInsetsChanged(systemBars.top)
applySidebarInsets(systemBars)
_binding?.root?.applyBottomWindowInsetsPadding(windowInsets)
val root = _binding?.root ?: return
val initial = root.getOrStoreInitialPadding()
root.updatePadding(bottom = initial.bottom + systemBars.bottom)
}

private fun applyImmersiveModeInsets(systemBars: Insets) {
val content = _binding?.content ?: return
val baseMargin = SizeUtils.dp2px(16f)
val isRtl = content.root.layoutDirection == View.LAYOUT_DIRECTION_RTL
val endInset = if (isRtl) systemBars.left else systemBars.right

content.btnToggleTopBar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = baseMargin + systemBars.top
marginEnd = baseMargin + endInset
}

content.btnToggleBottomBar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = baseMargin + systemBars.bottom
marginEnd = baseMargin + endInset
}

content.bottomSheet.updatePadding(top = systemBars.top)
_binding?.content?.applyImmersiveModeInsets(systemBars)
}

private fun handleKeyboardInsets(imeInsets: Insets) {
Expand Down Expand Up @@ -558,13 +537,6 @@ abstract class BaseEditorActivity :
editorAppBarInsetTop = insets.top
}

private fun applySidebarInsets(systemBars: Insets) {
val sidebar = _binding?.drawerSidebar ?: return
val baseTop = sidebar.paddingTop - sidebarLastInsetTop
sidebarLastInsetTop = systemBars.top
sidebar.updatePadding(top = baseTop + systemBars.top)
}

@Subscribe(threadMode = MAIN)
open fun onInstallationResult(event: InstallationEvent.InstallationResultEvent) {
val intent = event.intent
Expand Down Expand Up @@ -696,14 +668,33 @@ abstract class BaseEditorActivity :
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
immersiveController?.onConfigurationChanged(newConfig)
window?.decorView?.let { ViewCompat.requestApplyInsets(it) }
reapplySystemBarInsetsFromRoot()
_binding?.content?.applyBottomSheetAnchorForOrientation(newConfig.orientation)
}

private fun reapplySystemBarInsetsFromRoot() {
val root = _binding?.root ?: return
val rootInsets = ViewCompat.getRootWindowInsets(root)
if (rootInsets == null) {
root.post { reapplySystemBarInsetsFromRoot() }
return
}

val systemBars = rootInsets.getInsets(WindowInsetsCompat.Type.systemBars())
applyStandardInsets(systemBars)
applyImmersiveModeInsets(systemBars)
}


private fun setupToolbar() {
// Set the project name in the title TextView
content.root.findViewById<TextView>(R.id.title_text)?.apply {
text = editorViewModel.getProjectName()
}

content.editorAppBarLayout.applyResponsiveAppBarInsets(content.editorAppbarContent)

// Set up the drawer toggle on the title toolbar (where the hamburger menu should be)
content.titleToolbar.apply {
val toggle =
Expand Down Expand Up @@ -755,7 +746,13 @@ abstract class BaseEditorActivity :
val insetsTop = systemBarInsets?.top ?: 0
val topInset = (insetsTop * (1f - progress)).roundToInt()

content.editorAppbarContent.updatePadding(top = topInset)
val isLandscape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE

if (isLandscape) {
content.editorAppbarContent.updatePadding(top = topInset)
} else {
content.editorAppBarLayout.updatePadding(top = topInset)
}

memUsageView.chart.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = (insetsTop * progress).roundToInt()
Expand Down Expand Up @@ -1111,6 +1108,7 @@ abstract class BaseEditorActivity :
ContentTranslatingDrawerLayout.TranslationBehavior.FULL
setScrimColor(Color.TRANSPARENT)
}
drawerSidebar.applyRootSystemInsetsAsPadding(applyTop = true)
}
}

Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/com/itsaky/androidide/ui/EditorBottomSheet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,19 @@ constructor(
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
}

fun resetOffsetAnchor() {
anchorOffset = 0
behavior.peekHeight = collapsedHeight.roundToInt()
behavior.expandedOffset = 0
binding.root.updatePadding(bottom = insetBottom)
binding.headerContainer.apply {
updatePaddingRelative(bottom = insetBottom)
updateLayoutParams<LayoutParams> {
height = (collapsedHeight + insetBottom).roundToInt()
}
}
}

fun onSlide(sheetOffset: Float) {
val heightScale =
if (sheetOffset >= COLLAPSE_HEADER_AT_OFFSET) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.itsaky.androidide.utils

import android.content.res.Configuration
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.doOnAttach
import androidx.core.view.updatePadding
import com.itsaky.androidide.R
import com.itsaky.androidide.databinding.ContentEditorBinding
import com.blankj.utilcode.util.SizeUtils
import androidx.core.graphics.Insets
import android.view.ViewGroup
import androidx.core.view.updateLayoutParams

data class InitialPadding(val left: Int, val top: Int, val right: Int, val bottom: Int)

/**
* Gets or stores the view's original padding to prevent infinite accumulation when applying insets.
*
* @return The original [InitialPadding].
*/
fun View.getOrStoreInitialPadding(): InitialPadding {
return (getTag(R.id.tag_initial_padding) as? InitialPadding)
?: InitialPadding(paddingLeft, paddingTop, paddingRight, paddingBottom).also {
setTag(R.id.tag_initial_padding, it)
}
}

/**
* Applies top window insets responsively. Hides the AppBar in landscape mode and adjusts [appbarContent].
* Forces an inset request on attach to prevent drawing behind system bars after activity recreation.
*
* @param appbarContent The inner content view to pad in landscape mode.
*/
fun View.applyResponsiveAppBarInsets(appbarContent: View) {
ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val isLandscape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE

if (isLandscape) {
view.updatePadding(top = 0)
appbarContent.updatePadding(top = insets.top)
} else {
view.updatePadding(top = insets.top)
appbarContent.updatePadding(top = 0)
}
windowInsets
}

doOnAttach { it.requestApplyInsets() }
}

/**
* Applies root system window insets as padding, preserving the view's initial padding.
* Useful for deeply nested views (like DrawerLayouts) where standard inset listeners fail.
*
* @param applyLeft Apply left inset.
* @param applyTop Apply top inset.
* @param applyRight Apply right inset.
* @param applyBottom Apply bottom inset.
*/
fun View.applyRootSystemInsetsAsPadding(
applyLeft: Boolean = false,
applyTop: Boolean = false,
applyRight: Boolean = false,
applyBottom: Boolean = false
) {
val initial = getOrStoreInitialPadding()

ViewCompat.setOnApplyWindowInsetsListener(this) { view, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())

view.updatePadding(
left = initial.left + if (applyLeft) insets.left else 0,
top = initial.top + if (applyTop) insets.top else 0,
right = initial.right + if (applyRight) insets.right else 0,
bottom = initial.bottom + if (applyBottom) insets.bottom else 0
)
windowInsets
}

doOnAttach { it.requestApplyInsets() }
}

/**
* Applies immersive mode insets to editor UI elements that float near system bars.
*
* Keeps toggle buttons and bottom sheet aligned with system bars (status/nav).
*/
fun ContentEditorBinding.applyImmersiveModeInsets(systemBars: Insets) {
val baseMargin = SizeUtils.dp2px(16f)
val isRtl = root.layoutDirection == View.LAYOUT_DIRECTION_RTL
val endInset = if (isRtl) systemBars.left else systemBars.right

btnToggleTopBar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = baseMargin + systemBars.top
marginEnd = baseMargin + endInset
}

btnToggleBottomBar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = baseMargin + systemBars.bottom
marginEnd = baseMargin + endInset
}

bottomSheet.updatePadding(top = systemBars.top)
}

/**
* Recomputes bottom sheet offsets based on the current app bar height.
*/
fun ContentEditorBinding.refreshBottomSheetAnchor() {
bottomSheet.setOffsetAnchor(editorAppBarLayout)
}

/**
* Allows the bottom sheet to expand fully (no app bar anchor).
*/
fun ContentEditorBinding.resetBottomSheetAnchor() {
bottomSheet.resetOffsetAnchor()
}

/**
* Applies the correct bottom sheet anchor based on orientation.
*/
fun ContentEditorBinding.applyBottomSheetAnchorForOrientation(orientation: Int) {
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
refreshBottomSheetAnchor()
} else {
resetBottomSheetAnchor()
}
}
4 changes: 4 additions & 0 deletions resources/src/main/res/values/ids.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="tag_initial_padding" type="id" />
</resources>
Loading