Skip to content

Commit b9e3eac

Browse files
committed
Replace old omnibar layouts and ViewModels with unified classes
1 parent d98cbff commit b9e3eac

File tree

9 files changed

+211
-2423
lines changed

9 files changed

+211
-2423
lines changed

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

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,12 @@ class Omnibar(
7171
when (omnibarPosition) {
7272
OmnibarPosition.TOP -> {
7373
// remove bottom variant
74-
binding.rootView.removeView(binding.singleOmnibarBottom)
74+
binding.rootView.removeView(binding.omnibarLayoutBottom)
7575
}
7676

7777
OmnibarPosition.BOTTOM -> {
7878
// remove top variant
79-
binding.rootView.removeView(binding.singleOmnibar)
79+
binding.rootView.removeView(binding.omnibarLayoutTop)
8080

8181
adjustCoordinatorLayoutBehaviorForBottomOmnibar()
8282
}
@@ -176,11 +176,11 @@ class Omnibar(
176176
val omnibarLayout: OmnibarLayout by lazy {
177177
when (omnibarPosition) {
178178
OmnibarPosition.TOP -> {
179-
binding.singleOmnibar
179+
binding.omnibarLayoutTop
180180
}
181181

182182
OmnibarPosition.BOTTOM -> {
183-
binding.singleOmnibarBottom
183+
binding.omnibarLayoutBottom
184184
}
185185
}
186186
}
@@ -287,13 +287,6 @@ class Omnibar(
287287
omnibarLayout.setLogoClickListener(logoClickListener)
288288
}
289289

290-
fun configureOmnibarItemPressedListeners(listener: OmnibarItemPressedListener) {
291-
val omnibar = omnibarLayout
292-
if (omnibar is SingleOmnibarLayout) {
293-
omnibar.setSingleOmnibarItemPressedListener(listener)
294-
}
295-
}
296-
297290
fun configureInputScreenLaunchListener(listener: InputScreenLaunchListener) {
298291
omnibarLayout.setInputScreenLaunchListener(listener)
299292
}

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

Lines changed: 186 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616

1717
package com.duckduckgo.app.browser.omnibar
1818

19+
import android.animation.ValueAnimator
1920
import android.annotation.SuppressLint
2021
import android.content.Context
2122
import android.graphics.Color
22-
import android.graphics.drawable.ColorDrawable
23+
import android.os.Build
2324
import android.text.Editable
2425
import android.transition.ChangeBounds
2526
import android.transition.Fade
@@ -29,6 +30,7 @@ import android.util.AttributeSet
2930
import android.view.KeyEvent
3031
import android.view.View
3132
import android.view.ViewGroup
33+
import android.view.ViewTreeObserver.OnGlobalLayoutListener
3234
import android.view.animation.OvershootInterpolator
3335
import android.view.inputmethod.EditorInfo
3436
import android.widget.FrameLayout
@@ -37,10 +39,13 @@ import android.widget.ProgressBar
3739
import android.widget.TextView
3840
import androidx.appcompat.widget.Toolbar
3941
import androidx.coordinatorlayout.widget.CoordinatorLayout
42+
import androidx.core.graphics.drawable.toDrawable
4043
import androidx.core.transition.doOnEnd
4144
import androidx.core.view.doOnLayout
45+
import androidx.core.view.isGone
4246
import androidx.core.view.isInvisible
4347
import androidx.core.view.isVisible
48+
import androidx.core.view.updateLayoutParams
4449
import androidx.lifecycle.LifecycleOwner
4550
import androidx.lifecycle.ViewModelProvider
4651
import androidx.lifecycle.findViewTreeLifecycleOwner
@@ -91,9 +96,11 @@ import com.duckduckgo.browser.ui.omnibar.OmnibarPosition
9196
import com.duckduckgo.common.ui.DuckDuckGoActivity
9297
import com.duckduckgo.common.ui.view.KeyboardAwareEditText
9398
import com.duckduckgo.common.ui.view.KeyboardAwareEditText.ShowSuggestionsListener
99+
import com.duckduckgo.common.ui.view.addBottomShadow
94100
import com.duckduckgo.common.ui.view.gone
95101
import com.duckduckgo.common.ui.view.hide
96102
import com.duckduckgo.common.ui.view.show
103+
import com.duckduckgo.common.ui.view.toPx
97104
import com.duckduckgo.common.utils.ConflatedJob
98105
import com.duckduckgo.common.utils.DispatcherProvider
99106
import com.duckduckgo.common.utils.FragmentViewModelFactory
@@ -102,9 +109,12 @@ import com.duckduckgo.common.utils.text.TextChangedWatcher
102109
import com.duckduckgo.di.scopes.FragmentScope
103110
import com.duckduckgo.duckchat.api.DuckAiFeatureState
104111
import com.duckduckgo.duckchat.api.DuckChat
112+
import com.duckduckgo.navigation.api.GlobalActivityStarter
105113
import com.duckduckgo.serp.logos.api.SerpEasterEggLogosToggles
106114
import com.duckduckgo.serp.logos.api.SerpLogos
107115
import com.google.android.material.appbar.AppBarLayout
116+
import com.google.android.material.card.MaterialCardView
117+
import dagger.android.support.AndroidSupportInjection
108118
import kotlinx.coroutines.flow.collectLatest
109119
import kotlinx.coroutines.flow.map
110120
import kotlinx.coroutines.launch
@@ -147,7 +157,7 @@ open class OmnibarLayout @JvmOverloads constructor(
147157
) : Decoration()
148158

149159
data class PrivacyShieldChanged(
150-
val privacyShield: com.duckduckgo.app.global.model.PrivacyShield,
160+
val privacyShield: PrivacyShieldState,
151161
) : Decoration()
152162

153163
data class HighlightOmnibarItem(
@@ -215,6 +225,9 @@ open class OmnibarLayout @JvmOverloads constructor(
215225
@Inject
216226
lateinit var serpEasterEggLogosToggles: SerpEasterEggLogosToggles
217227

228+
@Inject
229+
lateinit var globalActivityStarter: GlobalActivityStarter
230+
218231
@Inject
219232
lateinit var addressBarTrackersAnimationFeatureToggle: AddressBarTrackersAnimationFeatureToggle
220233

@@ -237,6 +250,54 @@ open class OmnibarLayout @JvmOverloads constructor(
237250
private var lastViewMode: Mode? = null
238251
private var stateBuffer: MutableList<StateChange> = mutableListOf()
239252

253+
private val omnibarCardShadow: MaterialCardView by lazy { findViewById(R.id.omniBarContainerShadow) }
254+
private val iconsContainer: View by lazy { findViewById(R.id.iconsContainer) }
255+
private val shieldIconPulseAnimationContainer: View by lazy { findViewById(R.id.shieldIconPulseAnimationContainer) }
256+
private val omniBarContentContainer: View by lazy { findViewById(R.id.omniBarContentContainer) }
257+
private val backIcon: ImageView by lazy { findViewById(R.id.backIcon) }
258+
private val customTabToolbarContainerWrapper: ViewGroup by lazy { findViewById(R.id.customTabToolbarContainerWrapper) }
259+
260+
private var isFindInPageVisible = false
261+
private val findInPageLayoutVisibilityChangeListener =
262+
OnGlobalLayoutListener {
263+
val isVisible = findInPage.findInPageContainer.isVisible
264+
if (isFindInPageVisible != isVisible) {
265+
isFindInPageVisible = isVisible
266+
if (isVisible) {
267+
onFindInPageShown()
268+
} else {
269+
onFindInPageHidden()
270+
}
271+
}
272+
}
273+
274+
private val experimentalOmnibarCardMarginTop by lazy {
275+
resources.getDimensionPixelSize(CommonR.dimen.omnibarCardMarginTop)
276+
}
277+
278+
private val experimentalOmnibarCardMarginBottom by lazy {
279+
resources.getDimensionPixelSize(CommonR.dimen.omnibarCardMarginBottom)
280+
}
281+
282+
private var focusAnimator: ValueAnimator? = null
283+
284+
val omnibarPosition: OmnibarPosition
285+
286+
init {
287+
inflate(context, R.layout.view_omnibar, this)
288+
289+
val attr = context.theme.obtainStyledAttributes(attrs, R.styleable.OmnibarLayout, defStyle, 0)
290+
omnibarPosition = OmnibarPosition.entries[attr.getInt(R.styleable.OmnibarLayout_omnibarPosition, 0)]
291+
292+
AndroidSupportInjection.inject(this)
293+
294+
renderPosition()
295+
296+
if (Build.VERSION.SDK_INT >= 28) {
297+
omnibarCardShadow.addBottomShadow()
298+
}
299+
}
300+
240301
internal val findInPage: IncludeFindInPageBinding by lazy {
241302
IncludeFindInPageBinding.bind(findViewById(R.id.findInPage))
242303
}
@@ -338,8 +399,6 @@ open class OmnibarLayout @JvmOverloads constructor(
338399
}
339400
}
340401

341-
open var omnibarPosition: OmnibarPosition = OmnibarPosition.TOP
342-
343402
private val smoothProgressAnimator by lazy { SmoothProgressAnimator(pageLoadingIndicator) }
344403

345404
protected val viewModel: OmnibarLayoutViewModel by lazy {
@@ -393,11 +452,14 @@ open class OmnibarLayout @JvmOverloads constructor(
393452
}
394453

395454
animatorHelper.setListener(this)
455+
findInPage.findInPageContainer.viewTreeObserver.addOnGlobalLayoutListener(findInPageLayoutVisibilityChangeListener)
396456
}
397457

398458
override fun onDetachedFromWindow() {
399459
conflatedStateJob.cancel()
400460
conflatedCommandJob.cancel()
461+
focusAnimator?.cancel()
462+
findInPage.findInPageContainer.viewTreeObserver.removeOnGlobalLayoutListener(findInPageLayoutVisibilityChangeListener)
401463
super.onDetachedFromWindow()
402464
}
403465

@@ -528,13 +590,17 @@ open class OmnibarLayout @JvmOverloads constructor(
528590
voiceSearchButton.setOnClickListener {
529591
omnibarItemPressedListener?.onVoiceSearchPressed()
530592
}
593+
backIcon.setOnClickListener {
594+
viewModel.onBackButtonPressed()
595+
omnibarItemPressedListener?.onBackButtonPressed()
596+
}
531597
}
532598

533599
fun setLogoClickListener(logoClickListener: Omnibar.LogoClickListener) {
534600
omnibarLogoClickedListener = logoClickListener
535601
}
536602

537-
open fun render(viewState: ViewState) {
603+
fun render(viewState: ViewState) {
538604
when (viewState.viewMode) {
539605
is CustomTab -> {
540606
renderCustomTabMode(viewState, viewState.viewMode)
@@ -551,14 +617,81 @@ open class OmnibarLayout @JvmOverloads constructor(
551617
lastSeenPrivacyShield = null
552618
}
553619

620+
if (viewState.hasFocus || isFindInPageVisible) {
621+
animateOmnibarFocusedState(focused = true)
622+
} else {
623+
animateOmnibarFocusedState(focused = false)
624+
}
625+
626+
omnibarCardShadow.isGone = viewState.viewMode is CustomTab && !isFindInPageVisible
627+
554628
renderButtons(viewState)
555629

556630
omniBarButtonTransitionSet.doOnEnd {
557631
omnibarTextInput.requestLayout()
558632
}
559633
}
560634

561-
open fun processCommand(command: OmnibarLayoutViewModel.Command) {
635+
private fun renderPosition() {
636+
when (omnibarPosition) {
637+
OmnibarPosition.TOP -> {
638+
if (Build.VERSION.SDK_INT < 28) {
639+
omnibarCardShadow.cardElevation = 2f.toPx(context)
640+
}
641+
642+
shieldIconPulseAnimationContainer.updateLayoutParams {
643+
(this as MarginLayoutParams).apply {
644+
if (addressBarTrackersAnimationFeatureToggle.feature().isEnabled()) {
645+
// TODO when the animation is made permanent we should add this adjustment to the actual layout
646+
marginStart = 1.toPx()
647+
}
648+
}
649+
}
650+
}
651+
652+
OmnibarPosition.BOTTOM -> {
653+
// When omnibar is at the bottom, we're adding an additional space at the top
654+
omnibarCardShadow.updateLayoutParams {
655+
(this as MarginLayoutParams).apply {
656+
topMargin = experimentalOmnibarCardMarginBottom
657+
bottomMargin = experimentalOmnibarCardMarginTop
658+
}
659+
}
660+
661+
iconsContainer.updateLayoutParams {
662+
(this as MarginLayoutParams).apply {
663+
topMargin = experimentalOmnibarCardMarginBottom
664+
bottomMargin = experimentalOmnibarCardMarginTop
665+
}
666+
}
667+
668+
shieldIconPulseAnimationContainer.updateLayoutParams {
669+
(this as MarginLayoutParams).apply {
670+
topMargin = experimentalOmnibarCardMarginBottom
671+
bottomMargin = experimentalOmnibarCardMarginTop
672+
if (addressBarTrackersAnimationFeatureToggle.feature().isEnabled()) {
673+
// TODO when the animation is made permanent we should add this adjustment to the actual layout
674+
marginStart = 1.toPx()
675+
}
676+
}
677+
}
678+
679+
shieldIconPulseAnimationContainer.setPadding(
680+
shieldIconPulseAnimationContainer.paddingLeft,
681+
shieldIconPulseAnimationContainer.paddingTop,
682+
shieldIconPulseAnimationContainer.paddingRight,
683+
6.toPx(),
684+
)
685+
686+
// Try to reduce the bottom omnibar material shadow when not using the custom shadow
687+
if (Build.VERSION.SDK_INT < 28) {
688+
omnibarCardShadow.cardElevation = 0.5f.toPx(context)
689+
}
690+
}
691+
}
692+
}
693+
694+
fun processCommand(command: Command) {
562695
when (command) {
563696
Command.CancelAnimations -> {
564697
cancelAddressBarAnimations()
@@ -673,7 +806,10 @@ open class OmnibarLayout @JvmOverloads constructor(
673806
}
674807
}
675808

676-
open fun renderButtons(viewState: ViewState) {
809+
fun renderButtons(viewState: ViewState) {
810+
if (viewState.showButtons) {
811+
}
812+
677813
val newTransitionState =
678814
TransitionState(
679815
showClearButton = viewState.showClearButton,
@@ -709,6 +845,18 @@ open class OmnibarLayout @JvmOverloads constructor(
709845
previousTransitionState = newTransitionState
710846

711847
enableTextInputClickCatcher(viewState.showTextInputClickCatcher)
848+
849+
val showBackArrow = viewState.hasFocus
850+
if (showBackArrow) {
851+
backIcon.show()
852+
searchIcon.gone()
853+
shieldIcon.gone()
854+
daxIcon.gone()
855+
globeIcon.gone()
856+
duckPlayerIcon.gone()
857+
} else {
858+
backIcon.hide()
859+
}
712860
}
713861

714862
private fun renderBrowserMode(viewState: ViewState) {
@@ -912,7 +1060,7 @@ open class OmnibarLayout @JvmOverloads constructor(
9121060
omniBarContainer.isPressed = enabled
9131061
}
9141062

915-
private fun configureCustomTabOmnibar(customTab: ViewMode.CustomTab) {
1063+
private fun configureCustomTabOmnibar(customTab: CustomTab) {
9161064
if (!customTabToolbarContainer.customTabToolbar.isVisible) {
9171065
customTabToolbarContainer.customTabCloseIcon.setOnClickListener {
9181066
omnibarItemPressedListener?.onCustomTabClosePressed()
@@ -924,8 +1072,8 @@ open class OmnibarLayout @JvmOverloads constructor(
9241072

9251073
omniBarContainer.hide()
9261074

927-
toolbar.background = ColorDrawable(customTab.toolbarColor)
928-
toolbarContainer.background = ColorDrawable(customTab.toolbarColor)
1075+
toolbar.background = customTab.toolbarColor.toDrawable()
1076+
toolbarContainer.background = customTab.toolbarColor.toDrawable()
9291077

9301078
customTabToolbarContainer.customTabToolbar.show()
9311079

@@ -1043,4 +1191,32 @@ open class OmnibarLayout @JvmOverloads constructor(
10431191
viewModel.onTextInputClickCatcherClicked()
10441192
}
10451193
}
1194+
1195+
private fun animateOmnibarFocusedState(focused: Boolean) {
1196+
// temporarily disable focus animation
1197+
}
1198+
1199+
private fun onFindInPageShown() {
1200+
omniBarContentContainer.hide()
1201+
customTabToolbarContainerWrapper.hide()
1202+
if (viewModel.viewState.value.viewMode is ViewMode.CustomTab) {
1203+
omniBarContainer.show()
1204+
browserMenu.gone()
1205+
}
1206+
animateOmnibarFocusedState(focused = true)
1207+
viewModel.onFindInPageRequested()
1208+
}
1209+
1210+
private fun onFindInPageHidden() {
1211+
omniBarContentContainer.show()
1212+
customTabToolbarContainerWrapper.show()
1213+
if (viewModel.viewState.value.viewMode is ViewMode.CustomTab) {
1214+
omniBarContainer.hide()
1215+
browserMenu.isVisible = viewModel.viewState.value.showBrowserMenu
1216+
}
1217+
if (!viewModel.viewState.value.hasFocus) {
1218+
animateOmnibarFocusedState(focused = false)
1219+
}
1220+
viewModel.onFindInPageDismissed()
1221+
}
10461222
}

0 commit comments

Comments
 (0)