diff --git a/app/src/main/java/org/mozilla/fenix/experiments/Experiments.kt b/app/src/main/java/org/mozilla/fenix/experiments/Experiments.kt index 2411f6345cb8..b42889dc4e2a 100644 --- a/app/src/main/java/org/mozilla/fenix/experiments/Experiments.kt +++ b/app/src/main/java/org/mozilla/fenix/experiments/Experiments.kt @@ -20,5 +20,6 @@ class ExperimentBranch { const val A1 = "a1" const val A2 = "a2" const val DEFAULT_BROWSER_TOOLBAR_MENU = "default_browser_toolbar_menu" + const val DEFAULT_BROWSER_NEW_TAB_BANNER = "default_browser_newtab_banner" } } diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 531ecb2d76f8..e9b8394684b8 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -225,7 +225,8 @@ class HomeFragment : Fragment() { ) ).getTip() }, - showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome + showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome, + showSetAsDefaultBrowserCard = components.settings.shouldShowSetAsDefaultBrowserCard() ) ) } diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt index f1284ee6a5d1..59b25cc40ef8 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragmentStore.kt @@ -48,7 +48,8 @@ data class HomeFragmentState( val mode: Mode, val topSites: List, val tip: Tip? = null, - val showCollectionPlaceholder: Boolean + val showCollectionPlaceholder: Boolean, + val showSetAsDefaultBrowserCard: Boolean ) : State sealed class HomeFragmentAction : Action { @@ -69,6 +70,7 @@ sealed class HomeFragmentAction : Action { data class TopSitesChange(val topSites: List) : HomeFragmentAction() data class RemoveTip(val tip: Tip) : HomeFragmentAction() object RemoveCollectionsPlaceholder : HomeFragmentAction() + object RemoveSetDefaultBrowserCard : HomeFragmentAction() } private fun homeFragmentStateReducer( @@ -102,5 +104,6 @@ private fun homeFragmentStateReducer( is HomeFragmentAction.RemoveCollectionsPlaceholder -> { state.copy(showCollectionPlaceholder = false) } + is HomeFragmentAction.RemoveSetDefaultBrowserCard -> state.copy(showSetAsDefaultBrowserCard = false) } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt index e8d22d4c27ba..74434731d32a 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt @@ -24,6 +24,7 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.NoCollectionsMessageVie import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.TabInCollectionViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSitePagerViewHolder +import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.ExperimentDefaultBrowserCardViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingAutomaticSignInViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingFinishViewHolder import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingHeaderViewHolder @@ -116,6 +117,8 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) { val state: OnboardingState.SignedOutCanAutoSignIn ) : AdapterItem(OnboardingAutomaticSignInViewHolder.LAYOUT_ID) + object ExperimentDefaultBrowserCard : AdapterItem(ExperimentDefaultBrowserCardViewHolder.LAYOUT_ID) + object OnboardingThemePicker : AdapterItem(OnboardingThemePickerViewHolder.LAYOUT_ID) object OnboardingTrackingProtection : AdapterItem(OnboardingTrackingProtectionViewHolder.LAYOUT_ID) @@ -207,6 +210,8 @@ class SessionControlAdapter( OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID -> OnboardingToolbarPositionPickerViewHolder( view ) + ExperimentDefaultBrowserCardViewHolder.LAYOUT_ID -> ExperimentDefaultBrowserCardViewHolder(view, interactor) + else -> throw IllegalStateException() } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt index e64efd86d230..c9e682d6b433 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt @@ -39,7 +39,7 @@ import org.mozilla.fenix.components.tips.Tip import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.nav -import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.ext.openSetDefaultBrowserOption import org.mozilla.fenix.home.HomeFragment import org.mozilla.fenix.home.HomeFragmentAction import org.mozilla.fenix.home.HomeFragmentDirections @@ -168,6 +168,18 @@ interface SessionControlController { * @see [CollectionInteractor.onCollectionMenuOpened] and [TopSiteInteractor.onTopSiteMenuOpened] */ fun handleMenuOpened() + + /** + * @see [ExperimentCardInteractor.onSetDefaultBrowserClicked] + */ + fun handleSetDefaultBrowser() { + } + + /** + * @see [ExperimentCardInteractor.onCloseExperimentCardClicked] + */ + fun handleCloseExperimentCard() { + } } @Suppress("TooManyFunctions", "LargeClass") @@ -554,4 +566,14 @@ class DefaultSessionControlController( ) navController.nav(R.id.homeFragment, directions) } + + override fun handleSetDefaultBrowser() { + settings.userDismissedExperimentCard = true + activity.openSetDefaultBrowserOption() + } + + override fun handleCloseExperimentCard() { + settings.userDismissedExperimentCard = true + fragmentStore.dispatch(HomeFragmentAction.RemoveSetDefaultBrowserCard) + } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt index 33bdd0f31a8c..c88fa0fc1641 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlInteractor.kt @@ -190,6 +190,18 @@ interface TopSiteInteractor { fun onTopSiteMenuOpened() } +interface ExperimentCardInteractor { + /** + * Called when set default browser button is clicked + */ + fun onSetDefaultBrowserClicked() + + /** + * Called when close button on experiment card + */ + fun onCloseExperimentCardClicked() +} + /** * Interactor for the Home screen. * Provides implementations for the CollectionInteractor, OnboardingInteractor, @@ -199,7 +211,7 @@ interface TopSiteInteractor { class SessionControlInteractor( private val controller: SessionControlController ) : CollectionInteractor, OnboardingInteractor, TopSiteInteractor, TipInteractor, - TabSessionInteractor, ToolbarInteractor { + TabSessionInteractor, ToolbarInteractor, ExperimentCardInteractor { override fun onCollectionAddTabTapped(collection: TabCollection) { controller.handleCollectionAddTabTapped(collection) } @@ -295,4 +307,12 @@ class SessionControlInteractor( override fun onTopSiteMenuOpened() { controller.handleMenuOpened() } + + override fun onSetDefaultBrowserClicked() { + controller.handleSetDefaultBrowser() + } + + override fun onCloseExperimentCardClicked() { + controller.handleCloseExperimentCard() + } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt index 1f22ca0640b1..859d23d1ac87 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlView.kt @@ -21,18 +21,23 @@ import org.mozilla.fenix.home.OnboardingState // This method got a little complex with the addition of the tab tray feature flag // When we remove the tabs from the home screen this will get much simpler again. -@Suppress("ComplexMethod") +@Suppress("ComplexMethod", "LongParameterList") private fun normalModeAdapterItems( topSites: List, collections: List, expandedCollections: Set, tip: Tip?, - showCollectionsPlaceholder: Boolean + showCollectionsPlaceholder: Boolean, + showSetAsDefaultBrowserCard: Boolean ): List { val items = mutableListOf() tip?.let { items.add(AdapterItem.TipItem(it)) } + if (showSetAsDefaultBrowserCard) { + items.add(AdapterItem.ExperimentDefaultBrowserCard) + } + if (topSites.isNotEmpty()) { items.add(AdapterItem.TopSitePager(topSites)) } @@ -110,7 +115,8 @@ private fun HomeFragmentState.toAdapterList(): List = when (mode) { collections, expandedCollections, tip, - showCollectionPlaceholder + showCollectionPlaceholder, + showSetAsDefaultBrowserCard ) is Mode.Private -> privateModeAdapterItems() is Mode.Onboarding -> onboardingAdapterItems(mode.state) diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/ExperimentDefaultBrowserCardViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/ExperimentDefaultBrowserCardViewHolder.kt new file mode 100644 index 000000000000..27476132e282 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/ExperimentDefaultBrowserCardViewHolder.kt @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding + +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import kotlinx.android.synthetic.main.experiment_default_browser.view.* +import org.mozilla.fenix.R +import org.mozilla.fenix.ext.increaseTapArea +import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor + +class ExperimentDefaultBrowserCardViewHolder( + view: View, + private val interactor: SessionControlInteractor +) : RecyclerView.ViewHolder(view) { + + init { + view.set_default_browser.setOnClickListener { + interactor.onSetDefaultBrowserClicked() + } + + view.close.apply { + increaseTapArea(CLOSE_BUTTON_EXTRA_DPS) + setOnClickListener { + interactor.onCloseExperimentCardClicked() + } + } + } + + companion object { + internal const val LAYOUT_ID = R.layout.experiment_default_browser + private const val CLOSE_BUTTON_EXTRA_DPS = 38 + } +} diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index f83591a40d96..21140828633d 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -33,8 +33,11 @@ import org.mozilla.fenix.components.metrics.MozillaProductDetector import org.mozilla.fenix.components.settings.counterPreference import org.mozilla.fenix.components.settings.featureFlagPreference import org.mozilla.fenix.components.toolbar.ToolbarPosition +import org.mozilla.fenix.experiments.ExperimentBranch +import org.mozilla.fenix.experiments.Experiments import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.getPreferenceKey +import org.mozilla.fenix.ext.withExperiment import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataOnQuitType import org.mozilla.fenix.settings.logins.SavedLoginsSortingStrategyMenu @@ -61,6 +64,7 @@ class Settings(private val appContext: Context) : PreferencesHolder { private const val ALLOWED_INT = 2 private const val CFR_COUNT_CONDITION_FOCUS_INSTALLED = 1 private const val CFR_COUNT_CONDITION_FOCUS_NOT_INSTALLED = 3 + private const val APP_LAUNCHES_TO_SHOW_DEFAULT_BROWSER_CARD = 3 const val ONE_DAY_MS = 60 * 60 * 24 * 1000L const val THREE_DAYS_MS = 3 * ONE_DAY_MS @@ -292,6 +296,31 @@ class Settings(private val appContext: Context) : PreferencesHolder { default = false ) + /** + * Shows if the user has chosen to close the set default browser experiment card + * on home screen or has clicked the set as default browser button. + */ + var userDismissedExperimentCard by booleanPreference( + appContext.getPreferenceKey(R.string.pref_key_experiment_card_home), + default = false + ) + + /** + * Shows if the set default browser experiment card should be shown on home screen. + */ + fun shouldShowSetAsDefaultBrowserCard(): Boolean { + val browsers = BrowsersCache.all(appContext) + val experiments = appContext.components.analytics.experiments + val isExperimentBranch = + experiments.withExperiment(Experiments.DEFAULT_BROWSER) { experimentBranch -> + (experimentBranch == ExperimentBranch.DEFAULT_BROWSER_NEW_TAB_BANNER) + } + return isExperimentBranch && + !userDismissedExperimentCard && + !browsers.isFirefoxDefaultBrowser && + numberOfAppLaunches > APP_LAUNCHES_TO_SHOW_DEFAULT_BROWSER_CARD + } + var listTabView by booleanPreference( appContext.getPreferenceKey(R.string.pref_key_tab_view_list), default = true diff --git a/app/src/main/res/layout/experiment_default_browser.xml b/app/src/main/res/layout/experiment_default_browser.xml new file mode 100644 index 000000000000..0d59545f5343 --- /dev/null +++ b/app/src/main/res/layout/experiment_default_browser.xml @@ -0,0 +1,46 @@ + + + + + + + +