Skip to content

Commit 1165b4f

Browse files
committed
WIP
1 parent 6590275 commit 1165b4f

File tree

26 files changed

+853
-31
lines changed

26 files changed

+853
-31
lines changed

app/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@ dependencies {
405405
implementation project(':dax-prompts-api')
406406
implementation project(':dax-prompts-impl')
407407

408+
implementation project(':serp-logos-api')
409+
408410
// Deprecated. TODO: Stop using this artifact.
409411
implementation "androidx.legacy:legacy-support-v4:_"
410412
debugImplementation Square.leakCanary.android

app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import androidx.core.net.toUri
7878
import androidx.core.text.HtmlCompat
7979
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
8080
import androidx.core.text.toSpannable
81+
import androidx.core.view.ViewCompat
8182
import androidx.core.view.isGone
8283
import androidx.core.view.isVisible
8384
import androidx.core.view.postDelayed
@@ -440,6 +441,9 @@ class BrowserTabFragment :
440441
@Inject
441442
lateinit var browserAutofill: BrowserAutofill
442443

444+
@Inject
445+
lateinit var serpLogoHandler:
446+
443447
@Inject
444448
lateinit var faviconManager: FaviconManager
445449

@@ -1097,6 +1101,17 @@ class BrowserTabFragment :
10971101
configureCustomTab()
10981102
configureEditModeChangeDetection()
10991103
configureInputScreenLauncher()
1104+
configureLogoClickListener()
1105+
}
1106+
1107+
private fun configureLogoClickListener() {
1108+
omnibar.configureLogoClickListener(
1109+
object : Omnibar.LogoClickListener {
1110+
override fun onClick(url: String) {
1111+
viewModel.onDynamicLogoClicked(url)
1112+
}
1113+
},
1114+
)
11001115
}
11011116

11021117
private fun configureInputScreenLauncher() {
@@ -2204,7 +2219,6 @@ class BrowserTabFragment :
22042219
is Command.LaunchBookmarksActivity -> {
22052220
browserActivity?.launchBookmarks()
22062221
}
2207-
22082222
is Command.StartTrackersExperimentShieldPopAnimation -> showTrackersExperimentShieldPopAnimation()
22092223
is Command.RefreshOmnibar -> renderer.refreshOmnibar()
22102224
is Command.LaunchInputScreen -> {
@@ -2213,7 +2227,18 @@ class BrowserTabFragment :
22132227
launchInputScreen(query = "")
22142228
}
22152229
}
2216-
else -> {
2230+
is Command.ExtractDDGLogo -> {
2231+
webView?.let {
2232+
serpLogoHandler.evaluate(
2233+
webView = it,
2234+
onSerpLogoEvaluated = { serpLogo ->
2235+
viewModel.onLogoReceived(serpLogo)
2236+
},
2237+
)
2238+
}
2239+
}
2240+
is Command.ShowEnlargedLogo -> launchEnlargedEasterEggLogoActivity(it.url)
2241+
null -> {
22172242
// NO OP
22182243
}
22192244
}
@@ -4828,6 +4853,18 @@ class BrowserTabFragment :
48284853
fun launchTabSwitcherAfterTabsUndeleted() {
48294854
viewModel.onLaunchTabSwitcherAfterTabsUndeletedRequest()
48304855
}
4856+
4857+
private fun launchEnlargedEasterEggLogoActivity(logoUrl: String) {
4858+
ViewCompat.setTransitionName(omnibar.daxIcon, logoUrl)
4859+
4860+
val intent = EnlargedEasterEggLogoActivity.intent(requireContext(), logoUrl, logoUrl)
4861+
val activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(
4862+
requireActivity(),
4863+
omnibar.daxIcon,
4864+
logoUrl,
4865+
)
4866+
startActivity(intent, activityOptions.toBundle())
4867+
}
48314868
}
48324869

48334870
private class JsOrientationHandler {

app/src/main/java/com/duckduckgo/app/browser/BrowserTabViewModel.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ import com.duckduckgo.app.browser.commands.Command.DownloadImage
8888
import com.duckduckgo.app.browser.commands.Command.EditWithSelectedQuery
8989
import com.duckduckgo.app.browser.commands.Command.EmailSignEvent
9090
import com.duckduckgo.app.browser.commands.Command.EscapeMaliciousSite
91+
import com.duckduckgo.app.browser.commands.Command.ExtractDDGLogo
9192
import com.duckduckgo.app.browser.commands.Command.ExtractUrlFromCloakedAmpLink
9293
import com.duckduckgo.app.browser.commands.Command.FindInPageCommand
9394
import com.duckduckgo.app.browser.commands.Command.GenerateWebViewPreviewImage
@@ -169,6 +170,8 @@ import com.duckduckgo.app.browser.defaultbrowsing.prompts.AdditionalDefaultBrows
169170
import com.duckduckgo.app.browser.duckplayer.DUCK_PLAYER_FEATURE_NAME
170171
import com.duckduckgo.app.browser.duckplayer.DUCK_PLAYER_PAGE_FEATURE_NAME
171172
import com.duckduckgo.app.browser.duckplayer.DuckPlayerJSHelper
173+
import com.duckduckgo.app.browser.easteregglogos.EasterEggLogosToggles
174+
import com.duckduckgo.app.browser.easteregglogos.SerpLogo
172175
import com.duckduckgo.app.browser.favicon.FaviconManager
173176
import com.duckduckgo.app.browser.favicon.FaviconSource.ImageFavicon
174177
import com.duckduckgo.app.browser.favicon.FaviconSource.UrlFavicon
@@ -476,6 +479,7 @@ class BrowserTabViewModel @Inject constructor(
476479
private val tabManager: TabManager,
477480
private val addressDisplayFormatter: AddressDisplayFormatter,
478481
private val onboardingDesignExperimentManager: OnboardingDesignExperimentManager,
482+
private val easterEggLogosToggles: EasterEggLogosToggles,
479483
) : WebViewClientListener,
480484
EditSavedSiteListener,
481485
DeleteBookmarkListener,
@@ -1912,6 +1916,18 @@ class BrowserTabViewModel @Inject constructor(
19121916
viewModelScope.launch {
19131917
onboardingDesignExperimentManager.onWebPageFinishedLoading(url)
19141918
}
1919+
1920+
evaluateSerpLogoState(url)
1921+
}
1922+
}
1923+
1924+
private fun evaluateSerpLogoState(url: String?) {
1925+
if (easterEggLogosToggles.feature().isEnabled()) {
1926+
if (url != null && duckDuckGoUrlDetector.isDuckDuckGoQueryUrl(url)) {
1927+
command.value = ExtractDDGLogo
1928+
} else {
1929+
omnibarViewState.value = currentOmnibarViewState().copy(serpLogo = null)
1930+
}
19151931
}
19161932
}
19171933

@@ -4308,6 +4324,15 @@ class BrowserTabViewModel @Inject constructor(
43084324
}
43094325
}
43104326

4327+
fun onLogoReceived(serpLogo: SerpLogo) {
4328+
logcat { "BrowserTabFragment: Received Serp Logo: $serpLogo" }
4329+
omnibarViewState.postValue(currentOmnibarViewState().copy(serpLogo = serpLogo))
4330+
}
4331+
4332+
fun onDynamicLogoClicked(url: String) {
4333+
command.value = Command.ShowEnlargedLogo(url)
4334+
}
4335+
43114336
companion object {
43124337
private const val FIXED_PROGRESS = 50
43134338

app/src/main/java/com/duckduckgo/app/browser/commands/Command.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,6 @@ sealed class Command {
280280
data object StartTrackersExperimentShieldPopAnimation : Command()
281281
data object RefreshOmnibar : Command()
282282
data object LaunchInputScreen : Command()
283+
data object ExtractDDGLogo : Command()
284+
data class ShowEnlargedLogo(val url: String) : Command()
283285
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import android.annotation.SuppressLint
2020
import android.text.Editable
2121
import android.view.MotionEvent
2222
import android.view.View
23+
import android.widget.ImageView
2324
import androidx.appcompat.widget.Toolbar
2425
import androidx.coordinatorlayout.widget.CoordinatorLayout
2526
import androidx.core.view.postDelayed
@@ -164,6 +165,10 @@ class Omnibar(
164165
)
165166
}
166167

168+
interface LogoClickListener {
169+
fun onClick(url: String)
170+
}
171+
167172
data class OmnibarTextState(
168173
val text: String,
169174
val hasFocus: Boolean,
@@ -249,6 +254,10 @@ class Omnibar(
249254
newOmnibar.shieldIconExperiment
250255
}
251256

257+
val daxIcon: ImageView by lazy {
258+
newOmnibar.daxIcon
259+
}
260+
252261
val textInputRootView: View by lazy {
253262
newOmnibar.omnibarTextInput.rootView
254263
}
@@ -299,6 +308,10 @@ class Omnibar(
299308
newOmnibar.setOmnibarItemPressedListener(listener)
300309
}
301310

311+
fun configureLogoClickListener(logoClickListener: LogoClickListener) {
312+
newOmnibar.setLogoClickListener(logoClickListener)
313+
}
314+
302315
fun configureOmnibarItemPressedListeners(listener: OmnibarItemPressedListener) {
303316
val omnibar = newOmnibar
304317
if (omnibar is FadeOmnibarLayout) {

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

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,15 @@ import androidx.lifecycle.findViewTreeViewModelStoreOwner
4848
import androidx.lifecycle.flowWithLifecycle
4949
import androidx.lifecycle.lifecycleScope
5050
import com.airbnb.lottie.LottieAnimationView
51+
import com.bumptech.glide.Glide
52+
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade
5153
import com.duckduckgo.anvil.annotations.InjectWith
5254
import com.duckduckgo.app.browser.PulseAnimation
5355
import com.duckduckgo.app.browser.R
5456
import com.duckduckgo.app.browser.SmoothProgressAnimator
5557
import com.duckduckgo.app.browser.databinding.IncludeCustomTabToolbarBinding
5658
import com.duckduckgo.app.browser.databinding.IncludeFindInPageBinding
59+
import com.duckduckgo.app.browser.easteregglogos.EasterEggLogosToggles
5760
import com.duckduckgo.app.browser.omnibar.Omnibar.OmnibarTextState
5861
import com.duckduckgo.app.browser.omnibar.Omnibar.ViewMode
5962
import com.duckduckgo.app.browser.omnibar.Omnibar.ViewMode.CustomTab
@@ -74,6 +77,7 @@ import com.duckduckgo.app.browser.omnibar.OmnibarLayoutViewModel.Command.StartEx
7477
import com.duckduckgo.app.browser.omnibar.OmnibarLayoutViewModel.Command.StartExperimentVariant2OrVariant3Animation
7578
import com.duckduckgo.app.browser.omnibar.OmnibarLayoutViewModel.Command.StartTrackersAnimation
7679
import com.duckduckgo.app.browser.omnibar.OmnibarLayoutViewModel.Command.StartVisualDesignTrackersAnimation
80+
import com.duckduckgo.app.browser.omnibar.OmnibarLayoutViewModel.LeadingIconState.EasterEggLogo
7781
import com.duckduckgo.app.browser.omnibar.OmnibarLayoutViewModel.LeadingIconState.PrivacyShield
7882
import com.duckduckgo.app.browser.omnibar.OmnibarLayoutViewModel.ViewState
7983
import com.duckduckgo.app.browser.omnibar.animations.BrowserTrackersAnimatorHelper
@@ -105,6 +109,7 @@ import com.duckduckgo.common.utils.text.TextChangedWatcher
105109
import com.duckduckgo.di.scopes.FragmentScope
106110
import com.duckduckgo.duckchat.api.DuckAiFeatureState
107111
import com.duckduckgo.duckchat.api.DuckChat
112+
import com.duckduckgo.mobile.android.R as CommonR
108113
import com.google.android.material.appbar.AppBarLayout
109114
import javax.inject.Inject
110115
import kotlinx.coroutines.flow.collectLatest
@@ -192,6 +197,9 @@ open class OmnibarLayout @JvmOverloads constructor(
192197
@Inject
193198
lateinit var onboardingDesignExperimentManager: OnboardingDesignExperimentManager
194199

200+
@Inject
201+
lateinit var easterEggLogosToggles: EasterEggLogosToggles
202+
195203
private var previousTransitionState: TransitionState? = null
196204

197205
private val lifecycleOwner: LifecycleOwner by lazy {
@@ -205,6 +213,7 @@ open class OmnibarLayout @JvmOverloads constructor(
205213
private var omnibarTextListener: Omnibar.TextListener? = null
206214
private var omnibarItemPressedListener: Omnibar.ItemPressedListener? = null
207215
private var omnibarInputScreenLaunchListener: Omnibar.InputScreenLaunchListener? = null
216+
private var omnibarLogoClickedListener: Omnibar.LogoClickListener? = null
208217

209218
private var decoration: Decoration? = null
210219
private var lastViewMode: Mode? = null
@@ -503,6 +512,10 @@ open class OmnibarLayout @JvmOverloads constructor(
503512
}
504513
}
505514

515+
fun setLogoClickListener(logoClickListener: Omnibar.LogoClickListener) {
516+
omnibarLogoClickedListener = logoClickListener
517+
}
518+
506519
open fun render(viewState: ViewState) {
507520
when (viewState.viewMode) {
508521
is CustomTab -> {
@@ -560,6 +573,10 @@ open class OmnibarLayout @JvmOverloads constructor(
560573
is LaunchInputScreen -> {
561574
omnibarInputScreenLaunchListener?.launchInputScreen(query = command.query)
562575
}
576+
577+
is Command.LogoClicked -> {
578+
onLogoClicked(command.url)
579+
}
563580
}
564581
}
565582

@@ -577,7 +594,7 @@ open class OmnibarLayout @JvmOverloads constructor(
577594
}
578595

579596
private fun renderLeadingIconState(viewState: ViewState) {
580-
when (viewState.leadingIconState) {
597+
when (val leadingIconState = viewState.leadingIconState) {
581598
OmnibarLayoutViewModel.LeadingIconState.Search -> {
582599
searchIcon.show()
583600
shieldIcon.gone()
@@ -602,7 +619,19 @@ open class OmnibarLayout @JvmOverloads constructor(
602619
}
603620

604621
OmnibarLayoutViewModel.LeadingIconState.Dax -> {
605-
daxIcon.show()
622+
if (easterEggLogosToggles.feature().isEnabled()) {
623+
with(daxIcon) {
624+
setOnClickListener(null)
625+
show()
626+
Glide.with(this)
627+
.load(CommonR.drawable.ic_ddg_logo)
628+
.transition(withCrossFade())
629+
.placeholder(daxIcon.drawable)
630+
.into(this)
631+
}
632+
} else {
633+
daxIcon.show()
634+
}
606635
shieldIcon.gone()
607636
shieldIconExperiment.gone()
608637
searchIcon.gone()
@@ -627,6 +656,23 @@ open class OmnibarLayout @JvmOverloads constructor(
627656
searchIcon.gone()
628657
duckPlayerIcon.show()
629658
}
659+
660+
is EasterEggLogo -> {
661+
daxIcon.show()
662+
Glide.with(daxIcon)
663+
.load(leadingIconState.logoUrl)
664+
.placeholder(daxIcon.drawable)
665+
.transition(withCrossFade())
666+
.into(daxIcon)
667+
daxIcon.setOnClickListener {
668+
viewModel.onDynamicLogoClicked()
669+
}
670+
globeIcon.gone()
671+
shieldIcon.gone()
672+
shieldIconExperiment.gone()
673+
searchIcon.gone()
674+
duckPlayerIcon.gone()
675+
}
630676
}
631677
}
632678

@@ -986,6 +1032,10 @@ open class OmnibarLayout @JvmOverloads constructor(
9861032
}
9871033
}
9881034

1035+
private fun onLogoClicked(url: String) {
1036+
omnibarLogoClickedListener?.onClick(url)
1037+
}
1038+
9891039
override fun measuredHeight(): Int {
9901040
return measuredHeight
9911041
}

0 commit comments

Comments
 (0)