Skip to content

Commit c105d8b

Browse files
authored
fix WebView offset when navigation bar enabled and keyboard visible (#5931)
Task/Issue URL: https://app.asana.com/1/137249556945/project/1208671518894266/task/1210010551182684?focus=true ### Description Fixes an issue where navigation bar, even when gone when keyboard is inflated, would still retain the offset applied to the web view content. The fix is to notify the coordinator layout that navigation bar changed, when its visibility changes, which doesn't happen by default. We already have handling of that available in the behavior that adjusts browsers padding: https://github.com/duckduckgo/Android/blob/c0e22478de53029ceb4f6f80188c97782a74562a/app/src/main/java/com/duckduckgo/app/browser/webview/BrowserContainerLayoutBehavior.kt#L53-L78 Furthermore I'm fixing the issue where `browserLayout` wasn't observing the bottom omnibar directly. Instead, it synced its offset with transitions in `BottomAppBarBehavior`, which only responds to web view scrolls. As a result, changes to the omnibar triggered by other sources were ignored. The bottom omnibar includes the bottom navigation bar, which we hide when the keyboard appears. This alters the omnibar's size, but since `BottomAppBarBehavior` only reacted to scroll events, it didn’t update the web view's padding/margin accordingly. I removed the hardcoded `browserLayout` adjustments tied to `BottomAppBarBehavior`. Instead, I updated `BrowserContainerLayoutBehavior` to apply correct padding for all bottom omnibar changes, whether triggered by scroll or size adjustments.
1 parent afa6a58 commit c105d8b

File tree

4 files changed

+41
-26
lines changed

4 files changed

+41
-26
lines changed

app/src/main/java/com/duckduckgo/app/browser/navigation/bar/BrowserNavigationBarViewIntegration.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import com.duckduckgo.common.ui.view.show
2929
import com.duckduckgo.common.utils.keyboardVisibilityFlow
3030
import kotlinx.coroutines.CoroutineScope
3131
import kotlinx.coroutines.Job
32+
import kotlinx.coroutines.flow.distinctUntilChanged
3233
import kotlinx.coroutines.launch
3334

3435
/**
@@ -88,11 +89,14 @@ class BrowserNavigationBarViewIntegration(
8889
// we're hiding the navigation bar when keyboard is shown,
8990
// to prevent it from being "pushed up" within the coordinator layout
9091
keyboardVisibilityJob = lifecycleScope.launch {
91-
omnibar.textInputRootView.keyboardVisibilityFlow().collect { keyboardVisible ->
92+
omnibar.textInputRootView.keyboardVisibilityFlow().distinctUntilChanged().collect { keyboardVisible ->
9293
if (keyboardVisible) {
9394
navigationBarView.gone()
9495
} else {
95-
navigationBarView.show()
96+
navigationBarView.postDelayed(
97+
{ navigationBarView.show() },
98+
BrowserTabFragment.KEYBOARD_DELAY,
99+
)
96100
}
97101
}
98102
}

app/src/main/java/com/duckduckgo/app/browser/navigation/bar/view/BrowserNavigationBarView.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewMo
4545
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarViewModel.ViewState
4646
import com.duckduckgo.app.browser.omnibar.experiments.FadeOmnibarLayout
4747
import com.duckduckgo.app.browser.omnibar.model.OmnibarPosition
48+
import com.duckduckgo.app.browser.webview.BrowserContainerLayoutBehavior
4849
import com.duckduckgo.common.ui.viewbinding.viewBinding
4950
import com.duckduckgo.common.utils.ConflatedJob
5051
import com.duckduckgo.common.utils.ViewViewModelFactory
@@ -62,6 +63,22 @@ class BrowserNavigationBarView @JvmOverloads constructor(
6263
defStyle: Int = 0,
6364
) : FrameLayout(context, attrs, defStyle), AttachedBehavior {
6465

66+
override fun setVisibility(visibility: Int) {
67+
val isVisibilityUpdated = this.visibility != visibility
68+
69+
super.setVisibility(visibility)
70+
71+
/**
72+
* This notifies all view behaviors that depend on the [BrowserNavigationBarView] to recalculate whenever the bar's visibility changes,
73+
* for example, we require that in [BrowserContainerLayoutBehavior] to remove the bottom inset when navigation bar disappears.
74+
* The base coordinator behavior doesn't notify dependent views when visibility changes, so we need to do that manually.
75+
*/
76+
val parent = parent
77+
if (isVisibilityUpdated && parent is CoordinatorLayout) {
78+
parent.dispatchDependentViewsChanged(this)
79+
}
80+
}
81+
6582
@Inject
6683
lateinit var viewModelFactory: ViewViewModelFactory
6784

app/src/main/java/com/duckduckgo/app/browser/omnibar/BottomAppBarBehavior.kt

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import android.util.AttributeSet
2323
import android.view.Gravity
2424
import android.view.View
2525
import android.view.animation.DecelerateInterpolator
26-
import android.widget.RelativeLayout
2726
import androidx.coordinatorlayout.widget.CoordinatorLayout
2827
import androidx.core.view.ViewCompat
2928
import androidx.core.view.ViewCompat.NestedScrollType
@@ -46,17 +45,13 @@ class BottomAppBarBehavior<V : View>(
4645
private var lastStartedType: Int = 0
4746
private var offsetAnimator: ValueAnimator? = null
4847

49-
private var browserLayout: RelativeLayout? = null
50-
5148
@SuppressLint("RestrictedApi")
5249
override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View): Boolean {
5350
if (dependency is Snackbar.SnackbarLayout) {
5451
updateSnackbar(child, dependency)
5552
}
5653

57-
if (dependency.id == R.id.browserLayout) {
58-
browserLayout = dependency as RelativeLayout
59-
} else if (dependency.id != R.id.webViewFullScreenContainer) {
54+
if (dependency.id != R.id.webViewFullScreenContainer) {
6055
offsetBottomByToolbar(dependency)
6156
}
6257

@@ -95,7 +90,6 @@ class BottomAppBarBehavior<V : View>(
9590
// only hide the app bar in the browser layout
9691
if (target.id == R.id.browserWebView) {
9792
toolbar.translationY = max(0f, min(toolbar.height.toFloat(), toolbar.translationY + dy))
98-
offsetBottomByToolbar(browserLayout)
9993
}
10094
}
10195
}
@@ -132,7 +126,6 @@ class BottomAppBarBehavior<V : View>(
132126
} else {
133127
val targetTranslation = if (expanded) 0f else omnibar.height().toFloat()
134128
omnibar.setTranslation(targetTranslation)
135-
offsetBottomByToolbar(browserLayout)
136129
}
137130
}
138131

@@ -149,7 +142,6 @@ class BottomAppBarBehavior<V : View>(
149142
offsetAnimator?.addUpdateListener { animation ->
150143
val animatedValue = animation.animatedValue as Float
151144
omnibar.setTranslation(animatedValue)
152-
offsetBottomByToolbar(browserLayout)
153145
}
154146

155147
val targetTranslation = if (isVisible) 0f else omnibar.height().toFloat()

app/src/main/java/com/duckduckgo/app/browser/webview/BrowserContainerLayoutBehavior.kt

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,21 @@ import android.content.Context
2020
import android.util.AttributeSet
2121
import android.view.View
2222
import androidx.coordinatorlayout.widget.CoordinatorLayout
23-
import androidx.core.view.isVisible
23+
import androidx.core.view.isGone
2424
import com.duckduckgo.app.browser.navigation.bar.view.BrowserNavigationBarView
25+
import com.duckduckgo.app.browser.omnibar.OmnibarLayout
26+
import com.duckduckgo.app.browser.omnibar.model.OmnibarPosition
2527
import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior
2628

2729
/**
28-
* A [ScrollingViewBehavior] that additionally observes the position of [BrowserNavigationBarView], if present,
29-
* and applies bottom padding to the target view equal to the visible height of the navigation bar.
30+
* A [ScrollingViewBehavior] that additionally observes the position of [BrowserNavigationBarView] or bottom [OmnibarLayout], if present,
31+
* and applies bottom padding to the target view equal to the visible height of the bottom element.
3032
*
31-
* This prevents the navigation bar from overlapping with, for example, content found in the web view.
33+
* This prevents the bottom element from overlapping with, for example, content found in the web view.
3234
*
33-
* Note: This behavior is intended for use with the top omnibar. When the bottom omnibar is used,
34-
* it already includes the navigation bar, so no additional coordination is required.
35+
* Note: [BrowserNavigationBarView] or bottom [OmnibarLayout] will never be children of the coordinator layout at the same time, so they won't be competing for updates:
36+
* - When top [OmnibarLayout] is used, [BrowserNavigationBarView] is added directly to the coordinator layout.
37+
* - When bottom [OmnibarLayout] is used, it comes embedded with the [BrowserNavigationBarView].
3538
*/
3639
class BrowserContainerLayoutBehavior(
3740
context: Context,
@@ -43,23 +46,19 @@ class BrowserContainerLayoutBehavior(
4346
child: View,
4447
dependency: View,
4548
): Boolean {
46-
return if (dependency is BrowserNavigationBarView) {
47-
true
48-
} else {
49-
super.layoutDependsOn(parent, child, dependency)
50-
}
49+
return dependency.isBrowserNavigationBar() || dependency.isBottomOmnibar() || super.layoutDependsOn(parent, child, dependency)
5150
}
5251

5352
override fun onDependentViewChanged(
5453
parent: CoordinatorLayout,
5554
child: View,
5655
dependency: View,
5756
): Boolean {
58-
return if (dependency is BrowserNavigationBarView) {
59-
val newBottomPadding = if (dependency.isVisible) {
60-
dependency.measuredHeight - dependency.translationY.toInt()
61-
} else {
57+
return if (dependency.isBrowserNavigationBar() || dependency.isBottomOmnibar()) {
58+
val newBottomPadding = if (dependency.isGone) {
6259
0
60+
} else {
61+
dependency.measuredHeight - dependency.translationY.toInt()
6362
}
6463
if (child.paddingBottom != newBottomPadding) {
6564
child.setPadding(
@@ -76,4 +75,7 @@ class BrowserContainerLayoutBehavior(
7675
super.onDependentViewChanged(parent, child, dependency)
7776
}
7877
}
78+
79+
private fun View.isBrowserNavigationBar(): Boolean = this is BrowserNavigationBarView
80+
private fun View.isBottomOmnibar(): Boolean = this is OmnibarLayout && this.omnibarPosition == OmnibarPosition.BOTTOM
7981
}

0 commit comments

Comments
 (0)