Skip to content

Commit

Permalink
Add banner to onboarding and handle clicks
Browse files Browse the repository at this point in the history
  • Loading branch information
CrisBarreiro committed Oct 24, 2024
1 parent 71f7be2 commit df513f9
Show file tree
Hide file tree
Showing 16 changed files with 191 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import com.duckduckgo.brokensite.api.ReportFlow as BrokenSiteModelReportFlow
import com.duckduckgo.browser.api.brokensite.BrokenSiteData.ReportFlow
import com.duckduckgo.browser.api.brokensite.BrokenSiteData.ReportFlow.DASHBOARD
import com.duckduckgo.browser.api.brokensite.BrokenSiteData.ReportFlow.MENU
import com.duckduckgo.browser.api.brokensite.BrokenSiteData.ReportFlow.PROMPT
import com.duckduckgo.browser.api.brokensite.BrokenSiteOpenerContext
import com.duckduckgo.common.utils.SingleLiveEvent
import com.duckduckgo.common.utils.extractDomain
Expand Down Expand Up @@ -285,4 +286,5 @@ private fun MutableLiveData<ViewState>.setProtectionsState(state: SiteProtection
private fun ReportFlow.mapToBrokenSiteModelReportFlow(): BrokenSiteModelReportFlow = when (this) {
MENU -> BrokenSiteModelReportFlow.MENU
DASHBOARD -> BrokenSiteModelReportFlow.DASHBOARD
PROMPT -> BrokenSiteModelReportFlow.PROMPT
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.duckduckgo.brokensite.api.BrokenSiteSender
import com.duckduckgo.brokensite.api.ReportFlow
import com.duckduckgo.brokensite.api.ReportFlow.DASHBOARD
import com.duckduckgo.brokensite.api.ReportFlow.MENU
import com.duckduckgo.brokensite.api.ReportFlow.PROMPT
import com.duckduckgo.browser.api.WebViewVersionProvider
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.common.utils.absoluteString
Expand Down Expand Up @@ -207,4 +208,5 @@ class BrokenSiteSubmitter @Inject constructor(
private fun ReportFlow.toStringValue(): String = when (this) {
DASHBOARD -> "dashboard"
MENU -> "menu"
PROMPT -> "prompt"
}
18 changes: 18 additions & 0 deletions app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ import com.duckduckgo.app.browser.webshare.WebShareChooser
import com.duckduckgo.app.browser.webview.WebContentDebugging
import com.duckduckgo.app.browser.webview.WebViewBlobDownloadFeature
import com.duckduckgo.app.browser.webview.safewebview.SafeWebViewFeature
import com.duckduckgo.app.cta.ui.BrokenSitePromptDialogCta
import com.duckduckgo.app.cta.ui.Cta
import com.duckduckgo.app.cta.ui.CtaViewModel
import com.duckduckgo.app.cta.ui.DaxBubbleCta
Expand Down Expand Up @@ -1711,6 +1712,7 @@ class BrowserTabFragment :
is Command.HideSSLError -> hideSSLWarning()
is Command.LaunchScreen -> launchScreen(it.screen, it.payload)
is Command.HideOnboardingDaxDialog -> hideOnboardingDaxDialog(it.onboardingCta)
is Command.HideBrokenSitePromptCta -> hideBrokenSitePromptCta(it.brokenSitePromptDialogCta)
is Command.ShowRemoveSearchSuggestionDialog -> showRemoveSearchSuggestionDialog(it.suggestion)
is Command.AutocompleteItemRemoved -> autocompleteItemRemoved()
is Command.OpenDuckPlayerSettings -> globalActivityStarter.start(binding.root.context, DuckPlayerSettingsNoParams)
Expand Down Expand Up @@ -2667,6 +2669,10 @@ class BrowserTabFragment :
onboardingCta.hideOnboardingCta(binding)
}

private fun hideBrokenSitePromptCta(brokenSitePromptDialogCta: BrokenSitePromptDialogCta) {
brokenSitePromptDialogCta.hideOnboardingCta(binding)
}

private fun hideDaxBubbleCta() {
newBrowserTab.browserBackground.setBackgroundResource(0)
daxDialogIntroBubbleCta.root.gone()
Expand Down Expand Up @@ -3944,6 +3950,7 @@ class BrowserTabFragment :
is HomePanelCta -> showHomeCta(configuration)
is DaxBubbleCta -> showDaxOnboardingBubbleCta(configuration)
is OnboardingDaxDialogCta -> showOnboardingDialogCta(configuration)
is BrokenSitePromptDialogCta -> showBrokenSitePromptCta(configuration)
}
}

Expand Down Expand Up @@ -3999,6 +4006,17 @@ class BrowserTabFragment :
viewModel.onCtaShown()
}

@SuppressLint("ClickableViewAccessibility")
private fun showBrokenSitePromptCta(configuration: BrokenSitePromptDialogCta) {
hideNewTab()
configuration.showBrokenSitePromptCta(
binding,
onReportBrokenSiteClicked = { viewModel.onUserClickCtaOkButton(configuration) },
onDismissCtaClicked = { viewModel.onUserClickCtaSecondaryButton(configuration) },
)
viewModel.onCtaShown()
}

private fun removeNewTabLayoutClickListener() {
newBrowserTab.newTabLayout.setOnClickListener(null)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import android.net.http.SslCertificate
import android.os.Message
import android.print.PrintAttributes
import android.provider.MediaStore
import android.util.Log
import android.util.Patterns
import android.view.ContextMenu
import android.view.MenuItem
Expand Down Expand Up @@ -100,6 +101,7 @@ import com.duckduckgo.app.browser.commands.Command.ExtractUrlFromCloakedAmpLink
import com.duckduckgo.app.browser.commands.Command.FindInPageCommand
import com.duckduckgo.app.browser.commands.Command.GenerateWebViewPreviewImage
import com.duckduckgo.app.browser.commands.Command.HandleNonHttpAppLink
import com.duckduckgo.app.browser.commands.Command.HideBrokenSitePromptCta
import com.duckduckgo.app.browser.commands.Command.HideKeyboard
import com.duckduckgo.app.browser.commands.Command.HideOnboardingDaxDialog
import com.duckduckgo.app.browser.commands.Command.HideSSLError
Expand Down Expand Up @@ -198,6 +200,7 @@ import com.duckduckgo.app.browser.viewstate.OmnibarViewState
import com.duckduckgo.app.browser.viewstate.PrivacyShieldViewState
import com.duckduckgo.app.browser.viewstate.SavedSiteChangedViewState
import com.duckduckgo.app.browser.webview.SslWarningLayout.Action
import com.duckduckgo.app.cta.ui.BrokenSitePromptDialogCta
import com.duckduckgo.app.cta.ui.Cta
import com.duckduckgo.app.cta.ui.CtaViewModel
import com.duckduckgo.app.cta.ui.DaxBubbleCta
Expand Down Expand Up @@ -252,6 +255,7 @@ import com.duckduckgo.autofill.impl.AutofillFireproofDialogSuppressor
import com.duckduckgo.browser.api.UserBrowserProperties
import com.duckduckgo.browser.api.brokensite.BrokenSiteData
import com.duckduckgo.browser.api.brokensite.BrokenSiteData.ReportFlow.MENU
import com.duckduckgo.browser.api.brokensite.BrokenSiteData.ReportFlow.PROMPT
import com.duckduckgo.common.utils.AppUrl
import com.duckduckgo.common.utils.DispatcherProvider
import com.duckduckgo.common.utils.SingleLiveEvent
Expand Down Expand Up @@ -797,6 +801,7 @@ class BrowserTabViewModel @Inject constructor(
}

fun onViewVisible() {
Log.d("Cris", "onViewVisible")
setAdClickActiveTabData(url)

// we expect refreshCta to be called when a site is fully loaded if browsingShowing -trackers data available-.
Expand Down Expand Up @@ -2771,6 +2776,8 @@ class BrowserTabViewModel @Inject constructor(
}

suspend fun refreshCta(): Cta? {
Log.d("Cris", "refreshCta")

if (currentGlobalLayoutState() is Browser) {
val isBrowserShowing = currentBrowserViewState().browserShowing
if (hasCtaBeenShownForCurrentPage.get() && isBrowserShowing) return null
Expand Down Expand Up @@ -2815,6 +2822,7 @@ class BrowserTabViewModel @Inject constructor(
is HomePanelCta.AddWidgetAuto, is HomePanelCta.AddWidgetInstructions -> LaunchAddWidget
is OnboardingDaxDialogCta -> onOnboardingCtaOkButtonClicked(cta)
is DaxBubbleCta -> onDaxBubbleCtaOkButtonClicked(cta)
is BrokenSitePromptDialogCta -> onBrokenSiteCtaOkButtonClicked(cta)
else -> null
}
onboardingCommand?.let {
Expand All @@ -2828,6 +2836,8 @@ class BrowserTabViewModel @Inject constructor(
if (cta is DaxBubbleCta.DaxPrivacyProCta) {
val updatedCta = refreshCta()
ctaViewState.value = currentCtaViewState().copy(cta = updatedCta)
} else if (cta is BrokenSitePromptDialogCta) {
onBrokenSiteCtaDismissButtonClicked(cta)
}
}
}
Expand Down Expand Up @@ -3568,6 +3578,22 @@ class BrowserTabViewModel @Inject constructor(
}
}

private fun onBrokenSiteCtaDismissButtonClicked(cta: BrokenSitePromptDialogCta): Command? {
onUserDismissedCta(cta)
viewModelScope.launch {
command.value = HideBrokenSitePromptCta(cta)
}
return null
}

private fun onBrokenSiteCtaOkButtonClicked(cta: BrokenSitePromptDialogCta): Command? {
viewModelScope.launch {
command.value = BrokenSiteFeedback(BrokenSiteData.fromSite(site, reportFlow = PROMPT))
command.value = HideBrokenSitePromptCta(cta)
}
return null
}

private fun onOnboardingCtaOkButtonClicked(onboardingCta: OnboardingDaxDialogCta): Command? {
onUserDismissedCta(onboardingCta)
return when (onboardingCta) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.duckduckgo.app.browser.history.NavigationHistoryEntry
import com.duckduckgo.app.browser.model.BasicAuthenticationCredentials
import com.duckduckgo.app.browser.model.BasicAuthenticationRequest
import com.duckduckgo.app.browser.viewstate.SavedSiteChangedViewState
import com.duckduckgo.app.cta.ui.BrokenSitePromptDialogCta
import com.duckduckgo.app.cta.ui.OnboardingDaxDialogCta
import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteEntity
import com.duckduckgo.autofill.api.domain.app.LoginCredentials
Expand Down Expand Up @@ -236,6 +237,7 @@ sealed class Command {
val payload: String,
) : Command()
data class HideOnboardingDaxDialog(val onboardingCta: OnboardingDaxDialogCta) : Command()
data class HideBrokenSitePromptCta(val brokenSitePromptDialogCta: BrokenSitePromptDialogCta) : Command()
data class ShowRemoveSearchSuggestionDialog(val suggestion: AutoCompleteSuggestion) : Command()
data object AutocompleteItemRemoved : Command()
object OpenDuckPlayerSettings : Command()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum class CtaId {
DAX_END,
DAX_FIRE_BUTTON_PULSE,
DEVICE_SHIELD_CTA,
BROKEN_SITE_PROMPT,
UNKNOWN,
}

Expand Down
50 changes: 50 additions & 0 deletions app/src/main/java/com/duckduckgo/app/cta/ui/Cta.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.duckduckgo.app.cta.ui

import android.animation.ObjectAnimator
import android.content.Context
import android.net.Uri
import android.view.View
Expand All @@ -25,6 +26,7 @@ import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.ViewCompat.animate
import androidx.transition.AutoTransition
import androidx.transition.TransitionManager
import com.duckduckgo.app.browser.R
Expand Down Expand Up @@ -686,6 +688,54 @@ sealed class HomePanelCta(
)
}

class BrokenSitePromptDialogCta : Cta {

override val ctaId: CtaId = CtaId.BROKEN_SITE_PROMPT
override val shownPixel: Pixel.PixelName? = AppPixelName.ONBOARDING_DAX_CTA_SHOWN
override val okPixel: Pixel.PixelName? = AppPixelName.ONBOARDING_DAX_CTA_OK_BUTTON
override val cancelPixel: Pixel.PixelName? = null

override fun pixelCancelParameters(): Map<String, String> = mapOf()

override fun pixelOkParameters(): Map<String, String> = mapOf()

override fun pixelShownParameters(): Map<String, String> = mapOf()

fun hideOnboardingCta(binding: FragmentBrowserTabBinding) {
val view = binding.includeBrokenSitePromptDialog.root
val fadeOutAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 1f, 0f).apply {
duration = 3000
addUpdateListener { animator ->
val alpha = animator.animatedValue as Float
if (alpha <= 0f) {
view.visibility = View.GONE
removeAllListeners()
}
}
}

fadeOutAnimator.start()
}

fun showBrokenSitePromptCta(
binding: FragmentBrowserTabBinding,
onReportBrokenSiteClicked: () -> Unit,
onDismissCtaClicked: () -> Unit,
) {
val daxDialog = binding.includeBrokenSitePromptDialog
daxDialog.root.apply {
visibility = View.VISIBLE
alpha = 0f
animate()
.alpha(1f)
.setDuration(300)
.start()
}
binding.includeBrokenSitePromptDialog.reportButton.setOnClickListener { onReportBrokenSiteClicked.invoke() }
binding.includeBrokenSitePromptDialog.dismissButton.setOnClickListener { onDismissCtaClicked.invoke() }
}
}

fun DaxCta.addCtaToHistory(newCta: String): String {
val param = onboardingStore.onboardingDialogJourney?.split("-").orEmpty().toMutableList()
val daysInstalled = minOf(appInstallStore.daysInstalled().toInt(), MAX_DAYS_ALLOWED)
Expand Down
9 changes: 7 additions & 2 deletions app/src/main/java/com/duckduckgo/app/cta/ui/CtaViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class CtaViewModel @Inject constructor(
): Cta? {
return withContext(dispatcher) {
if (isBrowserShowing) {
getDaxDialogCta(site)
getBrowserCta(site)
} else {
getHomeCta()
}
Expand Down Expand Up @@ -260,7 +260,7 @@ class CtaViewModel @Inject constructor(
}

@WorkerThread
private suspend fun getDaxDialogCta(site: Site?): Cta? {
private suspend fun getBrowserCta(site: Site?): Cta? {
val nonNullSite = site ?: return null

val host = nonNullSite.domain
Expand All @@ -273,6 +273,11 @@ class CtaViewModel @Inject constructor(
return null
}

if (!daxOnboardingActive() || hideTips() || extendedOnboardingFeatureToggles.noBrowserCtas().isEnabled()) {
Timber.d("Cris, refresh dialog can be shown")
return BrokenSitePromptDialogCta()
}

if (!canShowDaxDialogCta()) return null

// Trackers blocked
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/res/layout/fragment_browser_tab.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@
android:id="@+id/includeOnboardingDaxDialog"
layout="@layout/include_onboarding_view_dax_dialog"
android:visibility="gone" />

<include
android:id="@+id/includeBrokenSitePromptDialog"
layout="@layout/prompt_broken_site"
android:visibility="gone" />
</FrameLayout>

<com.duckduckgo.app.browser.ui.ScrollAwareRefreshLayout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ data class BrokenSite(
val jsPerformance: List<Double>?,
)

enum class ReportFlow { DASHBOARD, MENU }
enum class ReportFlow { DASHBOARD, MENU, PROMPT }
2 changes: 1 addition & 1 deletion broken-site/broken-site-impl/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ dependencies {
implementation project(path: ':broken-site-api')
implementation project(path: ':browser-api')
implementation project(path: ':di')
implementation project(':common-ui')
implementation project(path: ':common-ui')
implementation project(path: ':common-utils')
implementation project(path: ':statistics-api')
implementation project(path: ':app-build-config-api')
Expand Down
35 changes: 35 additions & 0 deletions broken-site/broken-site-impl/src/main/res/drawable/top_banner.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2024 DuckDuckGo
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="390dp"
android:height="116dp"
android:viewportWidth="390"
android:viewportHeight="116"
>
<group>
<clip-path
android:pathData="M0 0H390V116H0V0Z"
/>
<path
android:pathData="M0 0H390V116H0V0Z"
android:strokeWidth="2"
android:strokeColor="#DDDDDD"
/>
</group>
</vector>
Loading

0 comments on commit df513f9

Please sign in to comment.