Bug 1845357 - Remove the first run onboarding
parent
06ca0e760a
commit
0d2dfcb85a
@ -1,245 +0,0 @@
|
||||
/* 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.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.verifyDarkThemeApplied
|
||||
import org.mozilla.fenix.helpers.TestHelper.verifyKeyboardVisibility
|
||||
import org.mozilla.fenix.helpers.TestHelper.verifyLightThemeApplied
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
class OnboardingTest {
|
||||
private lateinit var mDevice: UiDevice
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private val privacyNoticeLink = "mozilla.org/en-US/privacy/firefox"
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule = HomeActivityTestRule.withDefaultSettingsOverrides()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
private fun getUITheme(): Boolean {
|
||||
val mode =
|
||||
activityTestRule.activity.resources?.configuration?.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK)
|
||||
|
||||
return when (mode) {
|
||||
Configuration.UI_MODE_NIGHT_YES -> true // dark theme is set
|
||||
Configuration.UI_MODE_NIGHT_NO -> false // dark theme is not set, using light theme
|
||||
else -> false // default option is light theme
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies the first run onboarding screen
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun firstRunScreenTest() {
|
||||
homeScreen {
|
||||
verifyHomeScreenAppBarItems()
|
||||
verifyHomeScreenWelcomeItems()
|
||||
verifyChooseYourThemeCard(
|
||||
isDarkThemeChecked = false,
|
||||
isLightThemeChecked = false,
|
||||
isAutomaticThemeChecked = true,
|
||||
)
|
||||
verifyToolbarPlacementCard(isBottomChecked = true, isTopChecked = false)
|
||||
verifySignInToSyncCard()
|
||||
verifyPrivacyProtectionCard(
|
||||
settings = activityTestRule.activity.getSettings(),
|
||||
isStandardChecked = true,
|
||||
isStrictChecked = false,
|
||||
)
|
||||
verifyPrivacyNoticeCard()
|
||||
verifyStartBrowsingSection()
|
||||
verifyNavigationToolbarItems("0")
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies the functionality of the onboarding Start Browsing button
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun startBrowsingButtonTest() {
|
||||
homeScreen {
|
||||
verifyStartBrowsingButton()
|
||||
}.clickStartBrowsingButton {
|
||||
verifySearchView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dismissOnboardingUsingSettingsTest() {
|
||||
homeScreen {
|
||||
verifyWelcomeHeader()
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
verifyGeneralHeading()
|
||||
}.goBack {
|
||||
verifyExistingTopSitesList()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dismissOnboardingUsingBookmarksTest() {
|
||||
homeScreen {
|
||||
verifyWelcomeHeader()
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
verifyBookmarksMenuView()
|
||||
navigateUp()
|
||||
}
|
||||
homeScreen {
|
||||
verifyExistingTopSitesList()
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Failing, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1807268")
|
||||
@Test
|
||||
fun dismissOnboardingUsingHelpTest() {
|
||||
homeScreen {
|
||||
verifyWelcomeHeader()
|
||||
}.openThreeDotMenu {
|
||||
}.openHelp {
|
||||
verifyHelpUrl()
|
||||
}.goBack {
|
||||
verifyExistingTopSitesList()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toolbarTapDoesntDismissOnboardingTest() {
|
||||
homeScreen {
|
||||
verifyStartBrowsingButton()
|
||||
}.openSearch {
|
||||
verifySearchView()
|
||||
verifyKeyboardVisibility()
|
||||
}.dismissSearchBar {
|
||||
verifyStartBrowsingButton()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dismissOnboardingWithPageLoadTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
homeScreen {
|
||||
verifyStartBrowsingButton()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.goToHomescreen {
|
||||
verifyHomeScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun chooseYourThemeCardTest() {
|
||||
homeScreen {
|
||||
verifyChooseYourThemeCard(
|
||||
isDarkThemeChecked = false,
|
||||
isLightThemeChecked = false,
|
||||
isAutomaticThemeChecked = true,
|
||||
)
|
||||
clickLightThemeButton()
|
||||
verifyChooseYourThemeCard(
|
||||
isDarkThemeChecked = false,
|
||||
isLightThemeChecked = true,
|
||||
isAutomaticThemeChecked = false,
|
||||
)
|
||||
verifyLightThemeApplied(getUITheme())
|
||||
clickDarkThemeButton()
|
||||
verifyChooseYourThemeCard(
|
||||
isDarkThemeChecked = true,
|
||||
isLightThemeChecked = false,
|
||||
isAutomaticThemeChecked = false,
|
||||
)
|
||||
verifyDarkThemeApplied(getUITheme())
|
||||
clickAutomaticThemeButton()
|
||||
verifyChooseYourThemeCard(
|
||||
isDarkThemeChecked = false,
|
||||
isLightThemeChecked = false,
|
||||
isAutomaticThemeChecked = true,
|
||||
)
|
||||
verifyLightThemeApplied(getUITheme())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun pickYourToolbarPlacementCardTest() {
|
||||
homeScreen {
|
||||
verifyToolbarPlacementCard(isBottomChecked = true, isTopChecked = false)
|
||||
clickTopToolbarPlacementButton()
|
||||
verifyToolbarPosition(defaultPosition = false)
|
||||
clickBottomToolbarPlacementButton()
|
||||
verifyToolbarPosition(defaultPosition = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun privacyProtectionByDefaultCardTest() {
|
||||
homeScreen {
|
||||
verifyPrivacyProtectionCard(
|
||||
settings = activityTestRule.activity.getSettings(),
|
||||
isStandardChecked = true,
|
||||
isStrictChecked = false,
|
||||
)
|
||||
clickStrictTrackingProtectionButton()
|
||||
verifyPrivacyProtectionCard(
|
||||
settings = activityTestRule.activity.getSettings(),
|
||||
isStandardChecked = false,
|
||||
isStrictChecked = true,
|
||||
)
|
||||
clickStandardTrackingProtectionButton()
|
||||
verifyPrivacyProtectionCard(
|
||||
settings = activityTestRule.activity.getSettings(),
|
||||
isStandardChecked = true,
|
||||
isStrictChecked = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun pickUpWhereYouLeftOffCardTest() {
|
||||
homeScreen {
|
||||
verifySignInToSyncCard()
|
||||
}.clickSignInButton {
|
||||
verifyTurnOnSyncMenu()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun youControlYourDataCardTest() {
|
||||
homeScreen {
|
||||
verifyPrivacyNoticeCard()
|
||||
}.clickPrivacyNoticeButton {
|
||||
verifyCustomTabToolbarTitle("Firefox Privacy Notice")
|
||||
}.goBackToOnboardingScreen {
|
||||
verifyPrivacyNoticeCard()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/* 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 mozilla.components.service.glean.private.NoExtras
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.OnboardingFinishBinding
|
||||
import org.mozilla.fenix.onboarding.interactor.OnboardingInteractor
|
||||
|
||||
class OnboardingFinishViewHolder(
|
||||
view: View,
|
||||
private val interactor: OnboardingInteractor,
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
init {
|
||||
val binding = OnboardingFinishBinding.bind(view)
|
||||
binding.finishButton.setOnClickListener {
|
||||
interactor.onFinishOnboarding(focusOnAddressBar = true)
|
||||
Onboarding.finish.record(NoExtras())
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.onboarding_finish
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
/* 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 org.mozilla.fenix.R
|
||||
|
||||
class OnboardingHeaderViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.onboarding_header
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/* 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.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import mozilla.components.support.ktx.android.content.getDrawableWithTint
|
||||
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelative
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.setBounds
|
||||
|
||||
/**
|
||||
* Sets the drawableStart of a header in an onboarding card.
|
||||
*/
|
||||
fun TextView.setOnboardingIcon(@DrawableRes id: Int) {
|
||||
val icon = context.getDrawableWithTint(id, context.getColorFromAttr(R.attr.iconActive))?.apply {
|
||||
val size = context.resources.getDimensionPixelSize(R.dimen.onboarding_header_icon_height_width)
|
||||
setBounds(size)
|
||||
}
|
||||
putCompoundDrawablesRelative(start = icon)
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/* 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.navigation.Navigation
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import mozilla.components.service.glean.private.NoExtras
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.accounts.FenixFxAEntryPoint
|
||||
import org.mozilla.fenix.databinding.OnboardingManualSigninBinding
|
||||
import org.mozilla.fenix.home.HomeFragmentDirections
|
||||
|
||||
class OnboardingManualSignInViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
private var binding: OnboardingManualSigninBinding = OnboardingManualSigninBinding.bind(view)
|
||||
|
||||
init {
|
||||
binding.fxaSignInButton.setOnClickListener {
|
||||
Onboarding.fxaManualSignin.record(NoExtras())
|
||||
|
||||
val directions = HomeFragmentDirections.actionGlobalTurnOnSync(
|
||||
entrypoint = FenixFxAEntryPoint.OnboardingManualSignIn,
|
||||
)
|
||||
Navigation.findNavController(view).navigate(directions)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.onboarding_manual_signin
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/* 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 mozilla.components.service.glean.private.NoExtras
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.OnboardingPrivacyNoticeBinding
|
||||
import org.mozilla.fenix.onboarding.interactor.OnboardingInteractor
|
||||
|
||||
class OnboardingPrivacyNoticeViewHolder(
|
||||
view: View,
|
||||
private val interactor: OnboardingInteractor,
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
init {
|
||||
val binding = OnboardingPrivacyNoticeBinding.bind(view)
|
||||
|
||||
binding.readButton.setOnClickListener {
|
||||
Onboarding.privacyNotice.record(NoExtras())
|
||||
interactor.onReadPrivacyNoticeClicked()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.onboarding_privacy_notice
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/* 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.content.Context
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.OnboardingSectionHeaderBinding
|
||||
|
||||
class OnboardingSectionHeaderViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
private val binding = OnboardingSectionHeaderBinding.bind(view)
|
||||
private val sectionHeader = binding.sectionHeaderText
|
||||
|
||||
fun bind(labelBuilder: (Context) -> String) {
|
||||
sectionHeader.text = labelBuilder(itemView.context)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.onboarding_section_header
|
||||
}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
/* 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.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.OnboardingThemePickerBinding
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
import org.mozilla.fenix.utils.view.addToRadioGroup
|
||||
|
||||
class OnboardingThemePickerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
init {
|
||||
val binding = OnboardingThemePickerBinding.bind(view)
|
||||
val radioLightTheme = binding.themeLightRadioButton
|
||||
val radioDarkTheme = binding.themeDarkRadioButton
|
||||
val radioFollowDeviceTheme = binding.themeAutomaticRadioButton
|
||||
|
||||
radioFollowDeviceTheme.key = if (SDK_INT >= Build.VERSION_CODES.P) {
|
||||
R.string.pref_key_follow_device_theme
|
||||
} else {
|
||||
R.string.pref_key_auto_battery_theme
|
||||
}
|
||||
|
||||
addToRadioGroup(
|
||||
radioLightTheme,
|
||||
radioDarkTheme,
|
||||
radioFollowDeviceTheme,
|
||||
)
|
||||
radioLightTheme.addIllustration(binding.themeLightImage)
|
||||
radioDarkTheme.addIllustration(binding.themeDarkImage)
|
||||
|
||||
binding.themeDarkImage.setOnClickListener {
|
||||
Onboarding.prefToggledThemePicker.record(
|
||||
Onboarding.PrefToggledThemePickerExtra(
|
||||
Theme.DARK.name,
|
||||
),
|
||||
)
|
||||
radioDarkTheme.performClick()
|
||||
}
|
||||
|
||||
binding.themeLightImage.setOnClickListener {
|
||||
Onboarding.prefToggledThemePicker.record(
|
||||
Onboarding.PrefToggledThemePickerExtra(
|
||||
Theme.LIGHT.name,
|
||||
),
|
||||
)
|
||||
radioLightTheme.performClick()
|
||||
}
|
||||
|
||||
val automaticTitle = view.context.getString(R.string.onboarding_theme_automatic_title)
|
||||
val automaticSummary = view.context.getString(R.string.onboarding_theme_automatic_summary)
|
||||
binding.clickableRegionAutomatic.contentDescription = "$automaticTitle $automaticSummary"
|
||||
|
||||
binding.clickableRegionAutomatic.setOnClickListener {
|
||||
Onboarding.prefToggledThemePicker.record(
|
||||
Onboarding.PrefToggledThemePickerExtra(
|
||||
Theme.FOLLOW_DEVICE.name,
|
||||
),
|
||||
)
|
||||
radioFollowDeviceTheme.performClick()
|
||||
}
|
||||
|
||||
radioLightTheme.onClickListener {
|
||||
Onboarding.prefToggledThemePicker.record(
|
||||
Onboarding.PrefToggledThemePickerExtra(
|
||||
Theme.LIGHT.name,
|
||||
),
|
||||
)
|
||||
setNewTheme(AppCompatDelegate.MODE_NIGHT_NO)
|
||||
}
|
||||
|
||||
radioDarkTheme.onClickListener {
|
||||
Onboarding.prefToggledThemePicker.record(
|
||||
Onboarding.PrefToggledThemePickerExtra(
|
||||
Theme.DARK.name,
|
||||
),
|
||||
)
|
||||
setNewTheme(AppCompatDelegate.MODE_NIGHT_YES)
|
||||
}
|
||||
|
||||
radioFollowDeviceTheme.onClickListener {
|
||||
Onboarding.prefToggledThemePicker.record(
|
||||
Onboarding.PrefToggledThemePickerExtra(
|
||||
Theme.FOLLOW_DEVICE.name,
|
||||
),
|
||||
)
|
||||
if (SDK_INT >= Build.VERSION_CODES.P) {
|
||||
setNewTheme(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
} else {
|
||||
setNewTheme(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)
|
||||
}
|
||||
}
|
||||
|
||||
with(view.context.settings()) {
|
||||
val radio: OnboardingRadioButton = when {
|
||||
shouldUseLightTheme -> {
|
||||
radioLightTheme
|
||||
}
|
||||
shouldUseDarkTheme -> {
|
||||
radioDarkTheme
|
||||
}
|
||||
else -> {
|
||||
radioFollowDeviceTheme
|
||||
}
|
||||
}
|
||||
radio.updateRadioValue(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setNewTheme(mode: Int) {
|
||||
if (AppCompatDelegate.getDefaultNightMode() == mode) return
|
||||
AppCompatDelegate.setDefaultNightMode(mode)
|
||||
with(itemView.context.components) {
|
||||
core.engine.settings.preferredColorScheme = core.getPreferredColorScheme()
|
||||
useCases.sessionUseCases.reload.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.onboarding_theme_picker
|
||||
|
||||
// The theme used for telemetry
|
||||
enum class Theme { LIGHT, DARK, FOLLOW_DEVICE }
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/* 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 org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarPosition
|
||||
import org.mozilla.fenix.databinding.OnboardingToolbarPositionPickerBinding
|
||||
import org.mozilla.fenix.ext.asActivity
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
import org.mozilla.fenix.utils.view.addToRadioGroup
|
||||
|
||||
class OnboardingToolbarPositionPickerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
init {
|
||||
val binding = OnboardingToolbarPositionPickerBinding.bind(view)
|
||||
|
||||
val radioTopToolbar = binding.toolbarTopRadioButton
|
||||
val radioBottomToolbar = binding.toolbarBottomRadioButton
|
||||
val radio: OnboardingRadioButton
|
||||
|
||||
addToRadioGroup(radioTopToolbar, radioBottomToolbar)
|
||||
radioTopToolbar.addIllustration(binding.toolbarTopImage)
|
||||
radioBottomToolbar.addIllustration(binding.toolbarBottomImage)
|
||||
|
||||
val settings = view.context.components.settings
|
||||
radio = when (settings.toolbarPosition) {
|
||||
ToolbarPosition.BOTTOM -> radioBottomToolbar
|
||||
ToolbarPosition.TOP -> radioTopToolbar
|
||||
}
|
||||
radio.updateRadioValue(true)
|
||||
|
||||
radioBottomToolbar.onClickListener {
|
||||
Onboarding.prefToggledToolbarPosition.record(
|
||||
Onboarding.PrefToggledToolbarPositionExtra(
|
||||
Position.BOTTOM.name,
|
||||
),
|
||||
)
|
||||
|
||||
itemView.context.asActivity()?.recreate()
|
||||
}
|
||||
|
||||
binding.toolbarBottomImage.setOnClickListener {
|
||||
Onboarding.prefToggledToolbarPosition.record(
|
||||
Onboarding.PrefToggledToolbarPositionExtra(
|
||||
Position.BOTTOM.name,
|
||||
),
|
||||
)
|
||||
|
||||
radioBottomToolbar.performClick()
|
||||
}
|
||||
|
||||
radioTopToolbar.onClickListener {
|
||||
Onboarding.prefToggledToolbarPosition.record(
|
||||
Onboarding.PrefToggledToolbarPositionExtra(
|
||||
Position.TOP.name,
|
||||
),
|
||||
)
|
||||
itemView.context.asActivity()?.recreate()
|
||||
}
|
||||
|
||||
binding.toolbarTopImage.setOnClickListener {
|
||||
Onboarding.prefToggledToolbarPosition.record(
|
||||
Onboarding.PrefToggledToolbarPositionExtra(
|
||||
Position.TOP.name,
|
||||
),
|
||||
)
|
||||
radioTopToolbar.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.onboarding_toolbar_position_picker
|
||||
|
||||
// Position of the toolbar used for telemetry
|
||||
enum class Position { TOP, BOTTOM }
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
/* 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.content.Context
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.OnboardingTrackingProtectionBinding
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
import org.mozilla.fenix.utils.view.addToRadioGroup
|
||||
|
||||
class OnboardingTrackingProtectionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private var standardTrackingProtection: OnboardingRadioButton
|
||||
private var strictTrackingProtection: OnboardingRadioButton
|
||||
private var descriptionText: TextView
|
||||
|
||||
init {
|
||||
val binding = OnboardingTrackingProtectionBinding.bind(view)
|
||||
binding.headerText.setOnboardingIcon(R.drawable.ic_onboarding_tracking_protection)
|
||||
|
||||
standardTrackingProtection = binding.trackingProtectionStandardOption
|
||||
strictTrackingProtection = binding.trackingProtectionStrictDefault
|
||||
descriptionText = binding.descriptionText
|
||||
|
||||
val isTCPPublic = view.context.settings().enabledTotalCookieProtectionCFR
|
||||
setupDescriptionText(view.context, isTCPPublic)
|
||||
|
||||
val isTrackingProtectionEnabled = view.context.settings().shouldUseTrackingProtection
|
||||
setupRadioGroup(isTrackingProtectionEnabled)
|
||||
updateRadioGroupState(isTrackingProtectionEnabled)
|
||||
}
|
||||
|
||||
private fun setupDescriptionText(context: Context, shuldUseNewDescription: Boolean) {
|
||||
if (!shuldUseNewDescription) {
|
||||
val appName = context.getString(R.string.app_name)
|
||||
descriptionText.text = context.getString(
|
||||
R.string.onboarding_tracking_protection_description_old,
|
||||
appName,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRadioGroup(isChecked: Boolean) {
|
||||
updateRadioGroupState(isChecked)
|
||||
|
||||
addToRadioGroup(standardTrackingProtection, strictTrackingProtection)
|
||||
|
||||
strictTrackingProtection.isChecked =
|
||||
itemView.context.settings().useStrictTrackingProtection
|
||||
standardTrackingProtection.isChecked =
|
||||
!itemView.context.settings().useStrictTrackingProtection
|
||||
|
||||
standardTrackingProtection.onClickListener {
|
||||
updateTrackingProtectionPolicy()
|
||||
Onboarding.prefToggledTrackingProt.record(
|
||||
Onboarding.PrefToggledTrackingProtExtra(
|
||||
Settings.STANDARD.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
strictTrackingProtection.onClickListener {
|
||||
updateTrackingProtectionPolicy()
|
||||
Onboarding.prefToggledTrackingProt.record(
|
||||
Onboarding.PrefToggledTrackingProtExtra(
|
||||
Settings.STRICT.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRadioGroupState(isChecked: Boolean) {
|
||||
standardTrackingProtection.isEnabled = isChecked
|
||||
strictTrackingProtection.isEnabled = isChecked
|
||||
}
|
||||
|
||||
private fun updateTrackingProtectionPolicy() {
|
||||
itemView.context?.components?.let {
|
||||
val policy = it.core.trackingProtectionPolicyFactory
|
||||
.createTrackingProtectionPolicy()
|
||||
it.useCases.settingsUseCases.updateTrackingProtection.invoke(policy)
|
||||
it.useCases.sessionUseCases.reload.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.onboarding_tracking_protection
|
||||
|
||||
// Tracking protection policy types used for telemetry
|
||||
enum class Settings { STRICT, STANDARD }
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/* 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.onboarding
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import mozilla.components.concept.sync.AccountObserver
|
||||
import mozilla.components.concept.sync.AuthType
|
||||
import mozilla.components.concept.sync.OAuthAccount
|
||||
import mozilla.components.concept.sync.Profile
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
||||
/**
|
||||
* Observes various account-related events and dispatches the [OnboardingState] based on whether
|
||||
* or not the account is authenticated.
|
||||
*/
|
||||
class OnboardingAccountObserver(
|
||||
private val context: Context,
|
||||
private val dispatchChanges: (state: OnboardingState) -> Unit,
|
||||
) : AccountObserver {
|
||||
|
||||
private val accountManager by lazy { context.components.backgroundServices.accountManager }
|
||||
|
||||
/**
|
||||
* Returns the current [OnboardingState] based on the account state.
|
||||
*/
|
||||
fun getOnboardingState(): OnboardingState {
|
||||
val account = accountManager.authenticatedAccount()
|
||||
|
||||
return if (account != null) {
|
||||
OnboardingState.SignedIn
|
||||
} else {
|
||||
OnboardingState.SignedOutNoAutoSignIn
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun emitChanges() {
|
||||
dispatchChanges(getOnboardingState())
|
||||
}
|
||||
|
||||
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) = emitChanges()
|
||||
override fun onAuthenticationProblems() = emitChanges()
|
||||
override fun onLoggedOut() = emitChanges()
|
||||
override fun onProfileUpdated(profile: Profile) = emitChanges()
|
||||
}
|
@ -1,245 +0,0 @@
|
||||
/* 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.onboarding
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.lib.state.ext.consumeFrom
|
||||
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.components.StoreProvider
|
||||
import org.mozilla.fenix.databinding.FragmentHomeBinding
|
||||
import org.mozilla.fenix.ext.hideToolbar
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.home.HomeMenuView
|
||||
import org.mozilla.fenix.home.PrivateBrowsingButtonView
|
||||
import org.mozilla.fenix.home.TabCounterView
|
||||
import org.mozilla.fenix.home.ToolbarView
|
||||
import org.mozilla.fenix.home.privatebrowsing.controller.DefaultPrivateBrowsingController
|
||||
import org.mozilla.fenix.home.toolbar.DefaultToolbarController
|
||||
import org.mozilla.fenix.home.toolbar.SearchSelectorBinding
|
||||
import org.mozilla.fenix.home.toolbar.SearchSelectorMenuBinding
|
||||
import org.mozilla.fenix.onboarding.controller.DefaultOnboardingController
|
||||
import org.mozilla.fenix.onboarding.interactor.DefaultOnboardingInteractor
|
||||
import org.mozilla.fenix.onboarding.view.OnboardingView
|
||||
import org.mozilla.fenix.search.toolbar.DefaultSearchSelectorController
|
||||
import org.mozilla.fenix.search.toolbar.SearchSelectorMenu
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* Displays the first run onboarding screen.
|
||||
*/
|
||||
class OnboardingFragment : Fragment() {
|
||||
|
||||
private var _binding: FragmentHomeBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val searchSelectorMenu by lazy {
|
||||
SearchSelectorMenu(
|
||||
context = requireContext(),
|
||||
interactor = interactor,
|
||||
)
|
||||
}
|
||||
|
||||
private val store: BrowserStore
|
||||
get() = requireComponents.core.store
|
||||
private val browsingModeManager
|
||||
get() = (activity as HomeActivity).browsingModeManager
|
||||
|
||||
private var _interactor: DefaultOnboardingInteractor? = null
|
||||
private val interactor: DefaultOnboardingInteractor
|
||||
get() = _interactor!!
|
||||
|
||||
private var onboardingView: OnboardingView? = null
|
||||
private var homeMenuView: HomeMenuView? = null
|
||||
private var tabCounterView: TabCounterView? = null
|
||||
private var toolbarView: ToolbarView? = null
|
||||
|
||||
private lateinit var onboardingStore: OnboardingStore
|
||||
private lateinit var onboardingAccountObserver: OnboardingAccountObserver
|
||||
|
||||
private val searchSelectorBinding = ViewBoundFeatureWrapper<SearchSelectorBinding>()
|
||||
private val searchSelectorMenuBinding = ViewBoundFeatureWrapper<SearchSelectorMenuBinding>()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||
|
||||
val activity = activity as HomeActivity
|
||||
|
||||
onboardingAccountObserver = OnboardingAccountObserver(
|
||||
context = requireContext(),
|
||||
dispatchChanges = ::dispatchOnboardingStateChanges,
|
||||
)
|
||||
|
||||
onboardingStore = StoreProvider.get(this) {
|
||||
OnboardingStore(
|
||||
initialState = OnboardingFragmentState(
|
||||
onboardingState = onboardingAccountObserver.getOnboardingState(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
_interactor = DefaultOnboardingInteractor(
|
||||
controller = DefaultOnboardingController(
|
||||
activity = activity,
|
||||
navController = findNavController(),
|
||||
onboarding = requireComponents.fenixOnboarding,
|
||||
crashReporter = requireComponents.analytics.crashReporter,
|
||||
),
|
||||
privateBrowsingController = DefaultPrivateBrowsingController(
|
||||
activity = activity,
|
||||
appStore = requireComponents.appStore,
|
||||
navController = findNavController(),
|
||||
),
|
||||
searchSelectorController = DefaultSearchSelectorController(
|
||||
activity = activity,
|
||||
navController = findNavController(),
|
||||
),
|
||||
toolbarController = DefaultToolbarController(
|
||||
activity = activity,
|
||||
store = store,
|
||||
navController = findNavController(),
|
||||
),
|
||||
)
|
||||
|
||||
toolbarView = ToolbarView(
|
||||
binding = binding,
|
||||
context = requireContext(),
|
||||
interactor = interactor,
|
||||
)
|
||||
|
||||
onboardingView = OnboardingView(
|
||||
containerView = binding.sessionControlRecyclerView,
|
||||
interactor = interactor,
|
||||
)
|
||||
|
||||
activity.themeManager.applyStatusBarTheme(activity)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
consumeFrom(onboardingStore) { state ->
|
||||
onboardingView?.update(state.onboardingState, requireComponents.fenixOnboarding.config)
|
||||
}
|
||||
|
||||
homeMenuView = HomeMenuView(
|
||||
view = view,
|
||||
context = view.context,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
homeActivity = activity as HomeActivity,
|
||||
navController = findNavController(),
|
||||
menuButton = WeakReference(binding.menuButton),
|
||||
hideOnboardingIfNeeded = { interactor.onFinishOnboarding(focusOnAddressBar = false) },
|
||||
).also { it.build() }
|
||||
|
||||
tabCounterView = TabCounterView(
|
||||
context = requireContext(),
|
||||
browsingModeManager = browsingModeManager,
|
||||
navController = findNavController(),
|
||||
tabCounter = binding.tabButton,
|
||||
)
|
||||
|
||||
consumeFrom(store) {
|
||||
tabCounterView?.update(it)
|
||||
}
|
||||
|
||||
toolbarView?.build()
|
||||
|
||||
PrivateBrowsingButtonView(
|
||||
button = binding.privateBrowsingButton,
|
||||
browsingModeManager = browsingModeManager,
|
||||
onClick = { mode ->
|
||||
interactor.onPrivateModeButtonClicked(mode, userHasBeenOnboarded = false)
|
||||
},
|
||||
)
|
||||
|
||||
searchSelectorBinding.set(
|
||||
feature = SearchSelectorBinding(
|
||||
context = view.context,
|
||||
binding = binding,
|
||||
browserStore = store,
|
||||
searchSelectorMenu = searchSelectorMenu,
|
||||
),
|
||||
owner = viewLifecycleOwner,
|
||||
view = binding.root,
|
||||
)
|
||||
|
||||
searchSelectorMenuBinding.set(
|
||||
feature = SearchSelectorMenuBinding(
|
||||
context = view.context,
|
||||
interactor = interactor,
|
||||
searchSelectorMenu = searchSelectorMenu,
|
||||
browserStore = store,
|
||||
),
|
||||
owner = viewLifecycleOwner,
|
||||
view = view,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
if (browsingModeManager.mode == BrowsingMode.Private) {
|
||||
activity?.window?.setBackgroundDrawableResource(R.drawable.private_home_background_gradient)
|
||||
}
|
||||
|
||||
hideToolbar()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
if (browsingModeManager.mode == BrowsingMode.Private) {
|
||||
activity?.window?.setBackgroundDrawable(
|
||||
ColorDrawable(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
R.color.fx_mobile_private_layer_color_1,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
|
||||
onboardingView = null
|
||||
homeMenuView = null
|
||||
tabCounterView = null
|
||||
toolbarView = null
|
||||
_interactor = null
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
homeMenuView?.dismissMenu()
|
||||
}
|
||||
|
||||
private fun dispatchOnboardingStateChanges(state: OnboardingState) {
|
||||
if (state != onboardingStore.state.onboardingState) {
|
||||
onboardingStore.dispatch(OnboardingAction.UpdateState(state))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
/* 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.onboarding
|
||||
|
||||
import android.content.Context
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.widget.AppCompatRadioButton
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.content.withStyledAttributes
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.setTextColor
|
||||
import org.mozilla.fenix.ext.setTextSize
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.utils.view.GroupableRadioButton
|
||||
import org.mozilla.fenix.utils.view.uncheckAll
|
||||
|
||||
class OnboardingRadioButton(
|
||||
context: Context,
|
||||
attrs: AttributeSet,
|
||||
) : AppCompatRadioButton(context, attrs), GroupableRadioButton {
|
||||
private val radioGroups = mutableListOf<GroupableRadioButton>()
|
||||
private var illustration: ImageView? = null
|
||||
private var clickListener: (() -> Unit)? = null
|
||||
var key: Int = 0
|
||||
var title: Int = 0
|
||||
var description: Int = 0
|
||||
|
||||
init {
|
||||
context.withStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.OnboardingRadioButton,
|
||||
0,
|
||||
0,
|
||||
) {
|
||||
key = getResourceId(R.styleable.OnboardingRadioButton_onboardingKey, 0)
|
||||
title = getResourceId(R.styleable.OnboardingRadioButton_onboardingKeyTitle, 0)
|
||||
description =
|
||||
getResourceId(R.styleable.OnboardingRadioButton_onboardingKeyDescription, 0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addToRadioGroup(radioButton: GroupableRadioButton) {
|
||||
radioGroups.add(radioButton)
|
||||
}
|
||||
|
||||
fun addIllustration(illustration: ImageView) {
|
||||
this.illustration = illustration
|
||||
}
|
||||
|
||||
fun onClickListener(listener: () -> Unit) {
|
||||
clickListener = listener
|
||||
}
|
||||
|
||||
init {
|
||||
setOnClickListener {
|
||||
updateRadioValue(true)
|
||||
toggleRadioGroups()
|
||||
clickListener?.invoke()
|
||||
}
|
||||
if (title != 0) {
|
||||
setRadioButtonText(context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setRadioButtonText(context: Context) {
|
||||
val builder = SpannableStringBuilder()
|
||||
|
||||
val spannableTitle = SpannableString(resources.getString(title))
|
||||
spannableTitle.setTextSize(context, TITLE_TEXT_SIZE)
|
||||
spannableTitle.setTextColor(context, R.attr.textPrimary)
|
||||
|
||||
builder.append(spannableTitle)
|
||||
|
||||
if (description != 0) {
|
||||
val spannableDescription = SpannableString(resources.getString(description))
|
||||
spannableDescription.setTextSize(context, DESCRIPTION_TEXT_SIZE)
|
||||
spannableDescription.setTextColor(context, R.attr.textSecondary)
|
||||
builder.append("\n")
|
||||
builder.append(spannableDescription)
|
||||
}
|
||||
this.text = builder
|
||||
}
|
||||
|
||||
override fun updateRadioValue(isChecked: Boolean) {
|
||||
this.isChecked = isChecked
|
||||
illustration?.let {
|
||||
it.isSelected = isChecked
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
context.settings().preferences.edit {
|
||||
putBoolean(context.getString(key), isChecked)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleRadioGroups() {
|
||||
if (isChecked) {
|
||||
radioGroups.uncheckAll()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TITLE_TEXT_SIZE = 16
|
||||
private const val DESCRIPTION_TEXT_SIZE = 14
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
/* 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.onboarding
|
||||
|
||||
/**
|
||||
* Describes various onboarding states.
|
||||
*/
|
||||
sealed class OnboardingState {
|
||||
/**
|
||||
* Signed out, without an option to auto-login using a shared FxA account.
|
||||
*/
|
||||
object SignedOutNoAutoSignIn : OnboardingState()
|
||||
|
||||
/**
|
||||
* Signed in.
|
||||
*/
|
||||
object SignedIn : OnboardingState()
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/* 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.onboarding
|
||||
|
||||
import mozilla.components.lib.state.Action
|
||||
import mozilla.components.lib.state.State
|
||||
import mozilla.components.lib.state.Store
|
||||
|
||||
/**
|
||||
* The [Store] for holding the [OnboardingFragmentState] and applying [OnboardingAction]s.
|
||||
*/
|
||||
class OnboardingStore(
|
||||
initialState: OnboardingFragmentState = OnboardingFragmentState(),
|
||||
) : Store<OnboardingFragmentState, OnboardingAction>(
|
||||
initialState = initialState,
|
||||
reducer = ::onboardingFragmentStateReducer,
|
||||
)
|
||||
|
||||
/**
|
||||
* The state used for managing [OnboardingFragment].
|
||||
*
|
||||
* @property onboardingState Describes the onboarding account state.
|
||||
*/
|
||||
data class OnboardingFragmentState(
|
||||
val onboardingState: OnboardingState = OnboardingState.SignedOutNoAutoSignIn,
|
||||
) : State
|
||||
|
||||
/**
|
||||
* Actions to dispatch through the [OnboardingStore] to modify the [OnboardingFragmentState]
|
||||
* through the [onboardingFragmentStateReducer].
|
||||
*/
|
||||
sealed class OnboardingAction : Action {
|
||||
/**
|
||||
* Updates the onboarding account state.
|
||||
*
|
||||
* @param state The onboarding account state to display.
|
||||
*/
|
||||
data class UpdateState(val state: OnboardingState) : OnboardingAction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces the onboarding state from the current state with the provided [action] to be performed.
|
||||
*
|
||||
* @param state The current onboarding state.
|
||||
* @param action The action to be performed on the state.
|
||||
* @return the new [OnboardingState] with the [action] executed.
|
||||
*/
|
||||
private fun onboardingFragmentStateReducer(
|
||||
state: OnboardingFragmentState,
|
||||
action: OnboardingAction,
|
||||
): OnboardingFragmentState {
|
||||
return when (action) {
|
||||
is OnboardingAction.UpdateState -> state.copy(onboardingState = action.state)
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/* 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.onboarding.controller
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import mozilla.components.lib.crash.CrashReporter
|
||||
import org.mozilla.fenix.GleanMetrics.Events
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.ext.navigateWithBreadcrumb
|
||||
import org.mozilla.fenix.onboarding.FenixOnboarding
|
||||
import org.mozilla.fenix.onboarding.OnboardingFragmentDirections
|
||||
import org.mozilla.fenix.onboarding.interactor.OnboardingInteractor
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
|
||||
/**
|
||||
* An interface that handles the view manipulation of the first run onboarding.
|
||||
*/
|
||||
interface OnboardingController {
|
||||
/**
|
||||
* @see [OnboardingInteractor.onFinishOnboarding]
|
||||
*/
|
||||
fun handleFinishOnboarding(focusOnAddressBar: Boolean)
|
||||
|
||||
/**
|
||||
* @see [OnboardingInteractor.onReadPrivacyNoticeClicked]
|
||||
*/
|
||||
fun handleReadPrivacyNoticeClicked()
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation of [OnboardingController].
|
||||
*/
|
||||
class DefaultOnboardingController(
|
||||
private val activity: HomeActivity,
|
||||
private val navController: NavController,
|
||||
private val onboarding: FenixOnboarding,
|
||||
private val crashReporter: CrashReporter,
|
||||
) : OnboardingController {
|
||||
|
||||
override fun handleFinishOnboarding(focusOnAddressBar: Boolean) {
|
||||
onboarding.finish()
|
||||
|
||||
navController.navigateWithBreadcrumb(
|
||||
directions = OnboardingFragmentDirections.actionHome(focusOnAddressBar = focusOnAddressBar),
|
||||
navigateFrom = "OnboardingFragment",
|
||||
navigateTo = "ActionHome",
|
||||
crashReporter = crashReporter,
|
||||
)
|
||||
|
||||
if (focusOnAddressBar) {
|
||||
Events.searchBarTapped.record(Events.SearchBarTappedExtra("HOME"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleReadPrivacyNoticeClicked() {
|
||||
activity.startActivity(
|
||||
SupportUtils.createCustomTabIntent(
|
||||
activity,
|
||||
SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/* 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.onboarding.interactor
|
||||
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.home.HomeFragment
|
||||
import org.mozilla.fenix.home.HomeMenu
|
||||
import org.mozilla.fenix.home.privatebrowsing.controller.PrivateBrowsingController
|
||||
import org.mozilla.fenix.home.privatebrowsing.interactor.PrivateBrowsingInteractor
|
||||
import org.mozilla.fenix.home.toolbar.ToolbarController
|
||||
import org.mozilla.fenix.home.toolbar.ToolbarInteractor
|
||||
import org.mozilla.fenix.onboarding.controller.OnboardingController
|
||||
import org.mozilla.fenix.search.toolbar.SearchSelectorController
|
||||
import org.mozilla.fenix.search.toolbar.SearchSelectorInteractor
|
||||
import org.mozilla.fenix.search.toolbar.SearchSelectorMenu
|
||||
|
||||
/**
|
||||
* Interface for onboarding related actions.
|
||||
*/
|
||||
interface OnboardingInteractor {
|
||||
/**
|
||||
* Finishes the onboarding and navigates to the [HomeFragment]. Called when a user clicks on the
|
||||
* "Start Browsing" button or a menu item in the [HomeMenu].
|
||||
*
|
||||
* @param focusOnAddressBar Whether or not to focus the address bar when navigating to the
|
||||
* [HomeFragment].
|
||||
*/
|
||||
fun onFinishOnboarding(focusOnAddressBar: Boolean)
|
||||
|
||||
/**
|
||||
* Opens a custom tab to privacy notice url. Called when a user clicks on the "read our privacy notice" button.
|
||||
*/
|
||||
fun onReadPrivacyNoticeClicked()
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation of [OnboardingInteractor].
|
||||
*
|
||||
* @param controller An instance of [OnboardingController] which will be delegated for all user
|
||||
* interactions.
|
||||
*/
|
||||
class DefaultOnboardingInteractor(
|
||||
private val controller: OnboardingController,
|
||||
private val privateBrowsingController: PrivateBrowsingController,
|
||||
private val searchSelectorController: SearchSelectorController,
|
||||
private val toolbarController: ToolbarController,
|
||||
) : OnboardingInteractor,
|
||||
PrivateBrowsingInteractor,
|
||||
SearchSelectorInteractor,
|
||||
ToolbarInteractor {
|
||||
|
||||
override fun onFinishOnboarding(focusOnAddressBar: Boolean) {
|
||||
controller.handleFinishOnboarding(focusOnAddressBar)
|
||||
}
|
||||
|
||||
override fun onReadPrivacyNoticeClicked() {
|
||||
controller.handleReadPrivacyNoticeClicked()
|
||||
}
|
||||
|
||||
override fun onMenuItemTapped(item: SearchSelectorMenu.Item) {
|
||||
searchSelectorController.handleMenuItemTapped(item)
|
||||
}
|
||||
|
||||
override fun onLearnMoreClicked() {
|
||||
privateBrowsingController.handleLearnMoreClicked()
|
||||
}
|
||||
|
||||
override fun onPrivateModeButtonClicked(newMode: BrowsingMode, userHasBeenOnboarded: Boolean) {
|
||||
privateBrowsingController.handlePrivateModeButtonClicked(newMode, userHasBeenOnboarded)
|
||||
}
|
||||
|
||||
override fun onNavigateSearch() {
|
||||
toolbarController.handleNavigateSearch()
|
||||
}
|
||||
|
||||
override fun onPaste(clipboardText: String) {
|
||||
toolbarController.handlePaste(clipboardText)
|
||||
}
|
||||
|
||||
override fun onPasteAndGo(clipboardText: String) {
|
||||
toolbarController.handlePasteAndGo(clipboardText)
|
||||
}
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
/* 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.onboarding.view
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.home.BottomSpacerViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingFinishViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingHeaderViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingManualSignInViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingPrivacyNoticeViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingSectionHeaderViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingThemePickerViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingToolbarPositionPickerViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder
|
||||
import org.mozilla.fenix.onboarding.interactor.OnboardingInteractor
|
||||
|
||||
/**
|
||||
* Adapter for a list of onboarding views to be displayed.
|
||||
*/
|
||||
class OnboardingAdapter(
|
||||
private val interactor: OnboardingInteractor,
|
||||
) : ListAdapter<OnboardingAdapterItem, RecyclerView.ViewHolder>(DiffCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
|
||||
return when (viewType) {
|
||||
OnboardingHeaderViewHolder.LAYOUT_ID -> OnboardingHeaderViewHolder(view)
|
||||
OnboardingSectionHeaderViewHolder.LAYOUT_ID -> OnboardingSectionHeaderViewHolder(view)
|
||||
OnboardingManualSignInViewHolder.LAYOUT_ID -> OnboardingManualSignInViewHolder(view)
|
||||
OnboardingThemePickerViewHolder.LAYOUT_ID -> OnboardingThemePickerViewHolder(view)
|
||||
OnboardingTrackingProtectionViewHolder.LAYOUT_ID -> OnboardingTrackingProtectionViewHolder(
|
||||
view,
|
||||
)
|
||||
OnboardingPrivacyNoticeViewHolder.LAYOUT_ID -> OnboardingPrivacyNoticeViewHolder(
|
||||
view,
|
||||
interactor,
|
||||
)
|
||||
OnboardingFinishViewHolder.LAYOUT_ID -> OnboardingFinishViewHolder(view, interactor)
|
||||
OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID -> OnboardingToolbarPositionPickerViewHolder(
|
||||
view,
|
||||
)
|
||||
BottomSpacerViewHolder.LAYOUT_ID -> BottomSpacerViewHolder(view)
|
||||
else -> throw IllegalStateException("ViewType $viewType does not match a ViewHolder")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
|
||||
when (holder) {
|
||||
is OnboardingSectionHeaderViewHolder -> holder.bind(
|
||||
(item as OnboardingAdapterItem.OnboardingSectionHeader).labelBuilder,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int) = getItem(position).viewType
|
||||
|
||||
internal object DiffCallback : DiffUtil.ItemCallback<OnboardingAdapterItem>() {
|
||||
override fun areItemsTheSame(oldItem: OnboardingAdapterItem, newItem: OnboardingAdapterItem) =
|
||||
oldItem.sameAs(newItem)
|
||||
|
||||
@Suppress("DiffUtilEquals")
|
||||
override fun areContentsTheSame(
|
||||
oldItem: OnboardingAdapterItem,
|
||||
newItem: OnboardingAdapterItem,
|
||||
) = oldItem.contentsSameAs(newItem)
|
||||
|
||||
override fun getChangePayload(
|
||||
oldItem: OnboardingAdapterItem,
|
||||
newItem: OnboardingAdapterItem,
|
||||
): Any? {
|
||||
return oldItem.getChangePayload(newItem) ?: return super.getChangePayload(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum of the various onboarding views.
|
||||
*/
|
||||
sealed class OnboardingAdapterItem(@LayoutRes val viewType: Int) {
|
||||
/**
|
||||
* Onboarding top header.
|
||||
*/
|
||||
object OnboardingHeader : OnboardingAdapterItem(OnboardingHeaderViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
* Onboarding section header.
|
||||
*/
|
||||
data class OnboardingSectionHeader(
|
||||
val labelBuilder: (Context) -> String,
|
||||
) : OnboardingAdapterItem(OnboardingSectionHeaderViewHolder.LAYOUT_ID) {
|
||||
override fun sameAs(other: OnboardingAdapterItem) =
|
||||
other is OnboardingSectionHeader && labelBuilder == other.labelBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Onboarding sign into sync card.
|
||||
*/
|
||||
object OnboardingManualSignIn :
|
||||
OnboardingAdapterItem(OnboardingManualSignInViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
* Onboarding theme picker card.
|
||||
*/
|
||||
object OnboardingThemePicker : OnboardingAdapterItem(OnboardingThemePickerViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
* Onboarding tracking protection card.
|
||||
*/
|
||||
object OnboardingTrackingProtection :
|
||||
OnboardingAdapterItem(OnboardingTrackingProtectionViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
* Onboarding privacy card.
|
||||
*/
|
||||
object OnboardingPrivacyNotice :
|
||||
OnboardingAdapterItem(OnboardingPrivacyNoticeViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
* Onboarding start browsing button.
|
||||
*/
|
||||
object OnboardingFinish : OnboardingAdapterItem(OnboardingFinishViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
* Onboarding toolbar placement picker.
|
||||
*/
|
||||
object OnboardingToolbarPositionPicker :
|
||||
OnboardingAdapterItem(OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
* Spacer.
|
||||
*/
|
||||
object BottomSpacer : OnboardingAdapterItem(BottomSpacerViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
* Returns true if this item represents the same value as other.
|
||||
*/
|
||||
open fun sameAs(other: OnboardingAdapterItem) = this::class == other::class
|
||||
|
||||
/**
|
||||
* Returns a payload if there's been a change or null if not.
|
||||
*/
|
||||
open fun getChangePayload(newItem: OnboardingAdapterItem): Any? = null
|
||||
|
||||
/**
|
||||
* Returns true if this item represents the same value as the other.
|
||||
*/
|
||||
open fun contentsSameAs(other: OnboardingAdapterItem) = this::class == other::class
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
/* 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.onboarding.view
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.nimbus.OnboardingPanel
|
||||
import org.mozilla.fenix.onboarding.OnboardingState
|
||||
import org.mozilla.fenix.onboarding.interactor.OnboardingInteractor
|
||||
import org.mozilla.fenix.nimbus.Onboarding as OnboardingConfig
|
||||
|
||||
/**
|
||||
* Shows a list of onboarding cards.
|
||||
*/
|
||||
class OnboardingView(
|
||||
containerView: RecyclerView,
|
||||
interactor: OnboardingInteractor,
|
||||
) {
|
||||
|
||||
private val onboardingAdapter = OnboardingAdapter(interactor)
|
||||
|
||||
init {
|
||||
containerView.apply {
|
||||
adapter = onboardingAdapter
|
||||
layoutManager = LinearLayoutManager(containerView.context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the display of the onboarding cards based on the given [OnboardingState].
|
||||
*
|
||||
* @param onboardingState The new user onboarding page state.
|
||||
* @param onboardingConfig The new user onboarding page configuration.
|
||||
*/
|
||||
fun update(
|
||||
onboardingState: OnboardingState,
|
||||
onboardingConfig: OnboardingConfig,
|
||||
) {
|
||||
onboardingAdapter.submitList(onboardingAdapterItems(onboardingState, onboardingConfig))
|
||||
}
|
||||
|
||||
private fun onboardingAdapterItems(
|
||||
onboardingState: OnboardingState,
|
||||
onboardingConfig: OnboardingConfig,
|
||||
): List<OnboardingAdapterItem> {
|
||||
val items: MutableList<OnboardingAdapterItem> =
|
||||
mutableListOf(OnboardingAdapterItem.OnboardingHeader)
|
||||
|
||||
onboardingConfig.order.forEach {
|
||||
when (it) {
|
||||
OnboardingPanel.THEMES -> items.add(OnboardingAdapterItem.OnboardingThemePicker)
|
||||
OnboardingPanel.TOOLBAR_PLACEMENT -> items.add(OnboardingAdapterItem.OnboardingToolbarPositionPicker)
|
||||
// Customize FxA items based on where we are with the account state:
|
||||
OnboardingPanel.SYNC -> if (onboardingState == OnboardingState.SignedOutNoAutoSignIn) {
|
||||
items.add(OnboardingAdapterItem.OnboardingManualSignIn)
|
||||
}
|
||||
|
||||
OnboardingPanel.TCP -> items.add(OnboardingAdapterItem.OnboardingTrackingProtection)
|
||||
OnboardingPanel.PRIVACY_NOTICE -> items.add(OnboardingAdapterItem.OnboardingPrivacyNotice)
|
||||
}
|
||||
}
|
||||
|
||||
items.addAll(
|
||||
listOf(
|
||||
OnboardingAdapterItem.OnboardingFinish,
|
||||
OnboardingAdapterItem.BottomSpacer,
|
||||
),
|
||||
)
|
||||
|
||||
return items
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?attr/onboardingSelected" android:state_selected="true" />
|
||||
<item android:color="?attr/onboardingDeselected" />
|
||||
</selector>
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false" android:color="?attr/textDisabled" />
|
||||
<item android:color="?attr/textPrimary"/>
|
||||
</selector>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,74 +0,0 @@
|
||||
<!-- 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/. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="136dp"
|
||||
android:height="80dp"
|
||||
android:viewportWidth="136"
|
||||
android:viewportHeight="80">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M127,20a12,12 0,0 1,0 -24H8A12,12 0,0 0,-4 8v64A12,12 0,0 0,8 84h119a12,12 0,0 0,12 -12V8a12,12 0,0 1,-12 12z"/>
|
||||
<path
|
||||
android:pathData="M8,4L128,4A4,4 0,0 1,132 8L132,72A4,4 0,0 1,128 76L8,76A4,4 0,0 1,4 72L4,8A4,4 0,0 1,8 4z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="16.32"
|
||||
android:startX="-61.12"
|
||||
android:endY="53.76"
|
||||
android:endX="143.68"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFF9100"/>
|
||||
<item android:offset="0.41" android:color="#FFF10366"/>
|
||||
<item android:offset="1" android:color="#FF6173FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</group>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M127,20a12,12 0,0 1,0 -24H8A12,12 0,0 0,-4 8v64A12,12 0,0 0,8 84h119a12,12 0,0 0,12 -12V8a12,12 0,0 1,-12 12z"/>
|
||||
<path
|
||||
android:pathData="M14,68h108a2,2 0,0 0,2 -2V4H12v62a2,2 0,0 0,2 2z"
|
||||
android:fillColor="#f9f9fb"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
<path
|
||||
android:pathData="M22,24h92a1.93,1.93 0,0 1,2 1.88v12.24a1.93,1.93 0,0 1,-2 1.88H22a1.93,1.93 0,0 1,-2 -1.88V25.88A1.93,1.93 0,0 1,22 24z"
|
||||
android:fillColor="#fff"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M114,24a1.93,1.93 0,0 1,2 1.88v12.24a1.93,1.93 0,0 1,-2 1.88H22a1.93,1.93 0,0 1,-2 -1.88V25.88A1.93,1.93 0,0 1,22 24h92m0,-1H22a2.94,2.94 0,0 0,-3 2.88v12.24A2.94,2.94 0,0 0,22 41h92a2.94,2.94 0,0 0,3 -2.88V25.88a2.94,2.94 0,0 0,-3 -2.88z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".2"/>
|
||||
<path
|
||||
android:pathData="M111.67,36.14l-3,-3a3.78,3.78 0,1 0,-0.71 0.72l3,3a0.51,0.51 0,0 0,0.71 -0.72zM105.67,33.6a2.78,2.78 0,1 1,2.78 -2.78,2.79 2.79,0 0,1 -2.82,2.78z"
|
||||
android:fillColor="#20123a"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M114.35,5A13,13 0,0 0,127 21a12.88,12.88 0,0 0,4 -0.63V72a3,3 0,0 1,-3 3H8a3,3 0,0 1,-3 -3V8a3,3 0,0 1,3 -3z"
|
||||
android:strokeAlpha=".08"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#20123a"/>
|
||||
<path
|
||||
android:pathData="M124,49H12v-1h112z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".2"/>
|
||||
<path
|
||||
android:pathData="M96,63v-9l7,4.5zM68.5,55a3.5,3.5 0,1 1,-3.5 3.5,3.5 3.5,0 0,1 3.5,-3.5zM42,55v7h-7v-7z"
|
||||
android:strokeAlpha="0.36"
|
||||
android:fillColor="#15141a"
|
||||
android:fillType="evenOdd"
|
||||
android:fillAlpha="0.36"/>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M127,20a12,12 0,0 1,0 -24H8A12,12 0,0 0,-4 8v64A12,12 0,0 0,8 84h119a12,12 0,0 0,12 -12V8a12,12 0,0 1,-12 12z"/>
|
||||
<path
|
||||
android:pathData="M8,2L128,2A6,6 0,0 1,134 8L134,72A6,6 0,0 1,128 78L8,78A6,6 0,0 1,2 72L2,8A6,6 0,0 1,8 2z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/onboarding_illustration_color"/>
|
||||
</group>
|
||||
</vector>
|
@ -1,82 +0,0 @@
|
||||
<!-- 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/. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="136dp"
|
||||
android:height="80dp"
|
||||
android:viewportWidth="136"
|
||||
android:viewportHeight="80">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M127,20a12,12 0,0 1,0 -24H8A12,12 0,0 0,-4 8v64A12,12 0,0 0,8 84h119a12,12 0,0 0,12 -12V8a12,12 0,0 1,-12 12z"/>
|
||||
<path
|
||||
android:pathData="M8,4L128,4A4,4 0,0 1,132 8L132,72A4,4 0,0 1,128 76L8,76A4,4 0,0 1,4 72L4,8A4,4 0,0 1,8 4z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="16.32"
|
||||
android:startX="-60.4"
|
||||
android:endY="53.76"
|
||||
android:endX="143.12"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFF9100"/>
|
||||
<item android:offset="0.41" android:color="#FFF10366"/>
|
||||
<item android:offset="1" android:color="#FF6173FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</group>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M127,20a12,12 0,0 1,0 -24H8A12,12 0,0 0,-4 8v64A12,12 0,0 0,8 84h119a12,12 0,0 0,12 -12V8a12,12 0,0 1,-12 12z"/>
|
||||
<path
|
||||
android:pathData="M122,12H14a2,2 0,0 0,-2 2v62h112V14a2,2 0,0 0,-2 -2z"
|
||||
android:fillColor="#f9f9fb"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
<path
|
||||
android:pathData="M124,52H12v-1h112z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".2"/>
|
||||
<path
|
||||
android:pathData="M114,26H22a1.93,1.93 0,0 0,-2 1.88v12.24A1.93,1.93 0,0 0,22 42h92a1.93,1.93 0,0 0,2 -1.88V27.88a1.93,1.93 0,0 0,-2 -1.88z"
|
||||
android:fillColor="#fff"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M22,26a1.93,1.93 0,0 0,-2 1.88v12.24A1.93,1.93 0,0 0,22 42h92a1.93,1.93 0,0 0,2 -1.88V27.88a1.93,1.93 0,0 0,-2 -1.88H22m0,-1h92a2.94,2.94 0,0 1,3 2.88v12.24a2.94,2.94 0,0 1,-3 2.88H22a2.94,2.94 0,0 1,-3 -2.88V27.88A2.94,2.94 0,0 1,22 25z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".2"/>
|
||||
<path
|
||||
android:pathData="M112.86,38.14l-3,-3a3.81,3.81 0,1 0,-0.72 0.72l3,3a0.51,0.51 0,0 0,0.72 -0.72zM106.86,35.6a2.78,2.78 0,1 1,2.78 -2.78,2.78 2.78,0 0,1 -2.82,2.78z"
|
||||
android:fillColor="#20123a"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M25,15a2,2 0,1 0,2 2,2 2,0 0,0 -2,-2zM16,15l2.5,4 2.5,-4zM30,15v4h4v-4z"
|
||||
android:strokeAlpha="0.36"
|
||||
android:fillColor="#15141a"
|
||||
android:fillType="evenOdd"
|
||||
android:fillAlpha="0.36"/>
|
||||
<path
|
||||
android:pathData="M114.35,5A13,13 0,0 0,127 21a12.88,12.88 0,0 0,4 -0.63V72a3,3 0,0 1,-3 3H8a3,3 0,0 1,-3 -3V8a3,3 0,0 1,3 -3z"
|
||||
android:strokeAlpha=".08"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#20123a"/>
|
||||
<path
|
||||
android:pathData="M116.5,61L25.5,61A1.5,1.5 0,0 1,24 59.5L24,59.5A1.5,1.5 0,0 1,25.5 58L116.5,58A1.5,1.5 0,0 1,118 59.5L118,59.5A1.5,1.5 0,0 1,116.5 61z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".16"/>
|
||||
<path
|
||||
android:pathData="M116.5,69L42.5,69A1.5,1.5 0,0 1,41 67.5L41,67.5A1.5,1.5 0,0 1,42.5 66L116.5,66A1.5,1.5 0,0 1,118 67.5L118,67.5A1.5,1.5 0,0 1,116.5 69z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".16"/>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M127,20a12,12 0,0 1,0 -24H8A12,12 0,0 0,-4 8v64A12,12 0,0 0,8 84h119a12,12 0,0 0,12 -12V8a12,12 0,0 1,-12 12z"/>
|
||||
<path
|
||||
android:pathData="M8,2L128,2A6,6 0,0 1,134 8L134,72A6,6 0,0 1,128 78L8,78A6,6 0,0 1,2 72L2,8A6,6 0,0 1,8 2z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/onboarding_illustration_color"/>
|
||||
</group>
|
||||
</vector>
|
@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?iconActive"
|
||||
android:pathData="M12 6.5a3 3 0 1 0 0 6 3 3 0 0 0 0-6z" />
|
||||
<path
|
||||
android:fillColor="?iconActive"
|
||||
android:pathData="M12 21.5c-5.238 0-9.5-4.262-9.5-9.5S6.762 2.5 12 2.5s9.5 4.262 9.5 9.5-4.262 9.5-9.5 9.5zM12 4c-4.411 0-8 3.589-8 8s3.589 8 8 8 8-3.589 8-8-3.589-8-8-8z" />
|
||||
<path
|
||||
android:fillColor="?iconActive"
|
||||
android:pathData="M8.314 14c-0.652 0-1.232 0.269-1.672 0.684A6.014 6.014 0 0 0 12 18a6.355 6.355 0 0 0 1.084-0.104c0.083-0.014 0.165-0.033 0.247-0.051a6.014 6.014 0 0 0 4.027-3.162A2.43 2.43 0 0 0 15.686 14H8.314z" />
|
||||
</vector>
|
@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:name="vector"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12.0002,22C11.5342,22 11.0662,21.885 10.6462,21.668C8.4962,20.547 6.5242,18.735 5.0942,16.566C4.3852,15.493 3.8882,14.198 3.6162,12.716L2.9092,8.872C2.6692,7.563 3.2942,6.259 4.4632,5.625L10.5652,2.363C11.4632,1.88 12.5362,1.88 13.4322,2.359L19.5342,5.682C20.7082,6.313 21.3352,7.621 21.0912,8.936L20.3862,12.728C20.1152,14.201 19.6172,15.49 18.9112,16.56C17.4822,18.729 15.5102,20.542 13.3562,21.666C12.9372,21.885 12.4682,22 12.0002,22ZM11.5152,3.556L4.8452,7.13L4.3382,8.368L5.0922,12.445C5.3272,13.727 5.7502,14.836 6.3472,15.74C7.6392,17.7 9.4122,19.332 11.3392,20.337H12.6632C14.5932,19.329 16.3672,17.694 17.6582,15.735C18.2532,14.833 18.6752,13.729 18.9102,12.455L19.6632,8.368L19.0532,7.129L12.4942,3.557H11.5152V3.556Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="3"
|
||||
android:endY="22"
|
||||
android:startX="21"
|
||||
android:startY="2"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#FFAB71FF"
|
||||
android:offset="0" />
|
||||
<item
|
||||
android:color="#FF0250BB"
|
||||
android:offset="1" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</vector>
|
@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="?neutralFaded" />
|
||||
<gradient
|
||||
android:angle="45"
|
||||
android:endColor="?onboardingDarkGradientEndBackground"
|
||||
android:startColor="?onboardingDarkGradientStartBackground" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<stroke android:width="1dp" android:color="?neutralFaded" />
|
||||
<solid android:color="?attr/layer2" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- 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/. -->
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="#1F000000">
|
||||
<item
|
||||
android:bottom="6dp"
|
||||
android:top="6dp">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:attr/colorControlHighlight">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="#000000" />
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/tab_corner_radius"
|
||||
android:bottomRightRadius="@dimen/tab_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="?neutralFaded" />
|
||||
<solid android:color="?attr/layer2" />
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/tab_corner_radius"
|
||||
android:bottomRightRadius="@dimen/tab_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
@ -1,70 +0,0 @@
|
||||
<!-- 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/. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="136dp"
|
||||
android:height="80dp"
|
||||
android:viewportWidth="136"
|
||||
android:viewportHeight="80">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M9,20A12,12 0,0 0,9 -4h119a12,12 0,0 1,12 12v64a12,12 0,0 1,-12 12H9A12,12 0,0 1,-3 72V8A12,12 0,0 0,9 20z"/>
|
||||
<path
|
||||
android:pathData="M8,4L128,4A4,4 0,0 1,132 8L132,72A4,4 0,0 1,128 76L8,76A4,4 0,0 1,4 72L4,8A4,4 0,0 1,8 4z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="16.64"
|
||||
android:startX="197.12"
|
||||
android:endY="54.08"
|
||||
android:endX="-7.68"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFF9100"/>
|
||||
<item android:offset="0.41" android:color="#FFF10366"/>
|
||||
<item android:offset="1" android:color="#FF6173FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</group>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M9,20A12,12 0,0 0,9 -4h119a12,12 0,0 1,12 12v64a12,12 0,0 1,-12 12H9A12,12 0,0 1,-3 72V8A12,12 0,0 0,9 20z"/>
|
||||
<path
|
||||
android:pathData="M122,68H14a2,2 0,0 1,-2 -2V4h112v62a2,2 0,0 1,-2 2z"
|
||||
android:fillColor="#f9f9fb"/>
|
||||
</group>
|
||||
<path
|
||||
android:pathData="M22,24h92a1.93,1.93 0,0 1,2 1.88v12.24a1.93,1.93 0,0 1,-2 1.88H22a1.93,1.93 0,0 1,-2 -1.88V25.88A1.93,1.93 0,0 1,22 24z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M114,24a1.93,1.93 0,0 1,2 1.88v12.24a1.93,1.93 0,0 1,-2 1.88H22a1.93,1.93 0,0 1,-2 -1.88V25.88A1.93,1.93 0,0 1,22 24h92m0,-1H22a2.94,2.94 0,0 0,-3 2.88v12.24A2.94,2.94 0,0 0,22 41h92a2.94,2.94 0,0 0,3 -2.88V25.88a2.94,2.94 0,0 0,-3 -2.88z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".2"/>
|
||||
<path
|
||||
android:pathData="M32.86,36.14l-3,-3a3.81,3.81 0,1 0,-0.72 0.72l3,3a0.51,0.51 0,0 0,0.72 -0.72zM26.86,33.6a2.78,2.78 0,1 1,2.78 -2.78,2.78 2.78,0 0,1 -2.82,2.78z"
|
||||
android:fillColor="#20123a"/>
|
||||
<path
|
||||
android:pathData="M21.65,5a13,13 0,0 1,-3.46 12.19A13,13 0,0 1,5 20.37V72a3,3 0,0 0,3 3h120a3,3 0,0 0,3 -3V8a3,3 0,0 0,-3 -3z"
|
||||
android:strokeAlpha=".16"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#20123a"/>
|
||||
<path
|
||||
android:pathData="M12,48h112v1H12z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".2"/>
|
||||
<path
|
||||
android:pathData="M40,54v9l-7,-4.5zM67.5,55a3.5,3.5 0,1 1,-3.5 3.5,3.5 3.5,0 0,1 3.5,-3.5zM101,55v7h-7v-7z"
|
||||
android:strokeAlpha="0.36"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha="0.36"/>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M9,20A12,12 0,0 0,9 -4h119a12,12 0,0 1,12 12v64a12,12 0,0 1,-12 12H9A12,12 0,0 1,-3 72V8A12,12 0,0 0,9 20z"/>
|
||||
<path
|
||||
android:pathData="M8,2L128,2A6,6 0,0 1,134 8L134,72A6,6 0,0 1,128 78L8,78A6,6 0,0 1,2 72L2,8A6,6 0,0 1,8 2z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/onboarding_illustration_color"/>
|
||||
</group>
|
||||
</vector>
|
@ -1,78 +0,0 @@
|
||||
<!-- 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/. -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="136dp"
|
||||
android:height="80dp"
|
||||
android:viewportWidth="136"
|
||||
android:viewportHeight="80">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M9,20A12,12 0,0 0,9 -4h119a12,12 0,0 1,12 12v64a12,12 0,0 1,-12 12H9A12,12 0,0 1,-3 72V8A12,12 0,0 0,9 20z"/>
|
||||
<path
|
||||
android:pathData="M8,4L128,4A4,4 0,0 1,132 8L132,72A4,4 0,0 1,128 76L8,76A4,4 0,0 1,4 72L4,8A4,4 0,0 1,8 4z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="16.2"
|
||||
android:startX="196.4"
|
||||
android:endY="53.64"
|
||||
android:endX="-7.12"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FFFF9100"/>
|
||||
<item android:offset="0.41" android:color="#FFF10366"/>
|
||||
<item android:offset="1" android:color="#FF6173FF"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</group>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M9,20A12,12 0,0 0,9 -4h119a12,12 0,0 1,12 12v64a12,12 0,0 1,-12 12H9A12,12 0,0 1,-3 72V8A12,12 0,0 0,9 20z"/>
|
||||
<path
|
||||
android:pathData="M14,12h108a2,2 0,0 1,2 2v62H12V14a2,2 0,0 1,2 -2z"
|
||||
android:fillColor="#f9f9fb"/>
|
||||
</group>
|
||||
<path
|
||||
android:pathData="M12,51h112v1H12z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".2"/>
|
||||
<path
|
||||
android:pathData="M22,26h92a1.93,1.93 0,0 1,2 1.88v12.24a1.93,1.93 0,0 1,-2 1.88H22a1.93,1.93 0,0 1,-2 -1.88V27.88A1.93,1.93 0,0 1,22 26z"
|
||||
android:fillColor="#fff"/>
|
||||
<path
|
||||
android:pathData="M114,26a1.93,1.93 0,0 1,2 1.88v12.24a1.93,1.93 0,0 1,-2 1.88H22a1.93,1.93 0,0 1,-2 -1.88V27.88A1.93,1.93 0,0 1,22 26h92m0,-1H22a2.94,2.94 0,0 0,-3 2.88v12.24A2.94,2.94 0,0 0,22 43h92a2.94,2.94 0,0 0,3 -2.88V27.88a2.94,2.94 0,0 0,-3 -2.88z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".2"/>
|
||||
<path
|
||||
android:pathData="M32.86,38.14l-3,-3a3.81,3.81 0,1 0,-0.72 0.72l3,3a0.51,0.51 0,0 0,0.72 -0.72zM26.86,35.6a2.78,2.78 0,1 1,2.78 -2.78,2.78 2.78,0 0,1 -2.82,2.78z"
|
||||
android:fillColor="#20123a"/>
|
||||
<path
|
||||
android:pathData="M111,15a2,2 0,1 1,-2 2,2 2,0 0,1 2,-2zM120,15l-2.5,4 -2.5,-4zM106,15v4h-4v-4z"
|
||||
android:strokeAlpha="0.36"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha="0.36"/>
|
||||
<path
|
||||
android:pathData="M21.65,5a13,13 0,0 1,-3.46 12.19A13,13 0,0 1,5 20.37V72a3,3 0,0 0,3 3h120a3,3 0,0 0,3 -3V8a3,3 0,0 0,-3 -3z"
|
||||
android:strokeAlpha=".16"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#20123a"/>
|
||||
<path
|
||||
android:pathData="M19.5,58L110.5,58A1.5,1.5 0,0 1,112 59.5L112,59.5A1.5,1.5 0,0 1,110.5 61L19.5,61A1.5,1.5 0,0 1,18 59.5L18,59.5A1.5,1.5 0,0 1,19.5 58z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".16"/>
|
||||
<path
|
||||
android:pathData="M19.5,66L93.5,66A1.5,1.5 0,0 1,95 67.5L95,67.5A1.5,1.5 0,0 1,93.5 69L19.5,69A1.5,1.5 0,0 1,18 67.5L18,67.5A1.5,1.5 0,0 1,19.5 66z"
|
||||
android:fillColor="#15141a"
|
||||
android:fillAlpha=".16"/>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M9,20A12,12 0,0 0,9 -4h119a12,12 0,0 1,12 12v64a12,12 0,0 1,-12 12H9A12,12 0,0 1,-3 72V8A12,12 0,0 0,9 20z"/>
|
||||
<path
|
||||
android:pathData="M8,2L128,2A6,6 0,0 1,134 8L134,72A6,6 0,0 1,128 78L8,78A6,6 0,0 1,2 72L2,8A6,6 0,0 1,8 2z"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/onboarding_illustration_color"/>
|
||||
</group>
|
||||
</vector>
|
@ -1,33 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/conclusion"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/Header20TextStyle"
|
||||
android:text="@string/onboarding_conclusion_header"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/finish_button"
|
||||
style="@style/PositiveButton"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:background="@drawable/onboarding_padded_background"
|
||||
android:backgroundTint="?actionPrimary"
|
||||
android:text="@string/onboarding_finish"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/conclusion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/Header20TextStyle"
|
||||
android:text="@string/onboarding_header_2"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subheader_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/onboarding_message"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,56 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/onboarding_card"
|
||||
style="@style/OnboardingCardLightWithPadding"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/avatar_icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="12dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_onboarding_avatar_anonymous_large"
|
||||
tools:ignore="RtlSymmetry" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
app:layout_constraintTop_toTopOf="@id/avatar_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/avatar_icon"
|
||||
app:layout_constraintBottom_toBottomOf="@id/avatar_icon"
|
||||
android:layout_marginStart="52dp"
|
||||
android:text="@string/onboarding_account_sign_in_header" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="14dp"
|
||||
android:text="@string/onboarding_manual_sign_in_description"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
app:layout_constraintStart_toStartOf="@id/avatar_icon"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/fxa_sign_in_button"
|
||||
style="@style/NeutralOnboardingButton"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/onboarding_firefox_account_sign_in"
|
||||
app:layout_constraintTop_toBottomOf="@id/description_text"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/onboarding_card"
|
||||
style="@style/OnboardingCardLightWithPadding"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin">
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/onboarding_privacy_notice_header_1"
|
||||
android:drawablePadding="12dp"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
android:gravity="center_vertical"
|
||||
android:lines="1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
<TextView
|
||||
android:id="@+id/description_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
android:layout_marginTop="14dp"
|
||||
android:text="@string/onboarding_privacy_notice_description"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<Button
|
||||
style="@style/NeutralOnboardingButton"
|
||||
android:id="@+id/read_button"
|
||||
android:text="@string/onboarding_privacy_notice_read_button"
|
||||
android:layout_marginTop="10dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/description_text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/section_header_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
tools:text="@tools:sample/lorem"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"/>
|
@ -1,159 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/onboarding_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/OnboardingCardLight"
|
||||
android:paddingTop="16dp"
|
||||
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="14dp"
|
||||
android:text="@string/onboarding_theme_picker_header"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/onboarding_theme_picker_description_2"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/light_theme_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/onboarding_theme_light_title"
|
||||
android:textColor="?attr/textPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="@+id/theme_light_image"
|
||||
app:layout_constraintStart_toStartOf="@id/theme_light_image"
|
||||
app:layout_constraintTop_toBottomOf="@id/theme_light_image" />
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/theme_light_radio_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/onboarding_theme_light_title"
|
||||
android:elevation="1dp"
|
||||
android:theme="@style/Checkable.Colored"
|
||||
android:translationX="@dimen/onboarding_dual_pane_radio_button_translation_x"
|
||||
android:translationY="@dimen/onboarding_dual_pane_radio_button_translation_y"
|
||||
app:layout_constraintStart_toStartOf="@+id/theme_light_image"
|
||||
app:layout_constraintTop_toTopOf="@+id/theme_light_image"
|
||||
app:onboardingKey="@string/pref_key_light_theme" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/theme_light_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:contentDescription="@string/onboarding_theme_light_title"
|
||||
android:foreground="@drawable/rounded_ripple"
|
||||
app:layout_constraintEnd_toStartOf="@+id/theme_dark_image"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text"
|
||||
app:srcCompat="@drawable/onboarding_light_theme" />
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/theme_dark_radio_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/onboarding_theme_dark_title"
|
||||
android:elevation="1dp"
|
||||
android:theme="@style/Checkable.Colored"
|
||||
android:translationX="@dimen/onboarding_dual_pane_radio_button_translation_x"
|
||||
android:translationY="@dimen/onboarding_dual_pane_radio_button_translation_y"
|
||||
app:layout_constraintStart_toStartOf="@+id/theme_dark_image"
|
||||
app:layout_constraintTop_toTopOf="@+id/theme_dark_image"
|
||||
app:onboardingKey="@string/pref_key_dark_theme" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/theme_dark_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:contentDescription="@string/onboarding_theme_dark_title"
|
||||
android:foreground="@drawable/rounded_ripple"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/theme_light_image"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text"
|
||||
app:srcCompat="@drawable/onboarding_dark_theme" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dark_theme_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/onboarding_theme_dark_title"
|
||||
android:textColor="?attr/textPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="@+id/theme_dark_image"
|
||||
app:layout_constraintStart_toStartOf="@id/theme_dark_image"
|
||||
app:layout_constraintTop_toBottomOf="@id/theme_dark_image" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="1dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="?attr/borderPrimary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dark_theme_title" />
|
||||
|
||||
<View
|
||||
android:id="@+id/clickable_region_automatic"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@drawable/onboarding_rounded_bottom_corners_ripple"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/divider" />
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/theme_automatic_radio_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/onboarding_theme_automatic_title"
|
||||
android:foreground="@drawable/rounded_ripple"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:theme="@style/Checkable.Colored"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/divider"
|
||||
app:onboardingKey="@string/pref_key_follow_device_theme"
|
||||
app:onboardingKeyDescription="@string/onboarding_theme_automatic_summary"
|
||||
app:onboardingKeyTitle="@string/onboarding_theme_automatic_title"
|
||||
tools:text="Automatic" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,121 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/onboarding_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/OnboardingCardLight"
|
||||
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="14dp"
|
||||
android:text="@string/onboarding_toolbar_placement_header_1"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/onboarding_toolbar_placement_description"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text" />
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/toolbar_bottom_radio_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/preference_bottom_toolbar"
|
||||
android:elevation="1dp"
|
||||
android:theme="@style/Checkable.Colored"
|
||||
android:translationX="@dimen/onboarding_dual_pane_radio_button_translation_x"
|
||||
android:translationY="@dimen/onboarding_dual_pane_radio_button_translation_y"
|
||||
app:layout_constraintStart_toStartOf="@+id/toolbar_bottom_image"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text"
|
||||
app:layout_constraintTop_toTopOf="@+id/toolbar_bottom_image"
|
||||
app:onboardingKey="@string/pref_key_toolbar_bottom" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/toolbar_bottom_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:contentDescription="@string/preference_bottom_toolbar"
|
||||
android:foreground="@drawable/rounded_ripple"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintEnd_toStartOf="@+id/toolbar_top_image"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text"
|
||||
app:srcCompat="@drawable/onboarding_toolbar_bottom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/toolbar_bottom_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/preference_bottom_toolbar"
|
||||
android:textColor="?attr/textPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="@+id/toolbar_bottom_image"
|
||||
app:layout_constraintStart_toStartOf="@id/toolbar_bottom_image"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar_bottom_image"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/toolbar_top_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/preference_top_toolbar"
|
||||
android:textColor="?attr/textPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="@+id/toolbar_top_image"
|
||||
app:layout_constraintStart_toStartOf="@id/toolbar_top_image"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar_top_image"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/toolbar_top_radio_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/preference_top_toolbar"
|
||||
android:elevation="1dp"
|
||||
android:theme="@style/Checkable.Colored"
|
||||
android:translationX="@dimen/onboarding_dual_pane_radio_button_translation_x"
|
||||
android:translationY="@dimen/onboarding_dual_pane_radio_button_translation_y"
|
||||
app:layout_constraintStart_toStartOf="@+id/toolbar_top_image"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text"
|
||||
app:layout_constraintTop_toTopOf="@+id/toolbar_top_image"
|
||||
app:onboardingKey="@string/pref_key_toolbar_top" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/toolbar_top_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:contentDescription="@string/preference_top_toolbar"
|
||||
android:foreground="@drawable/rounded_ripple"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/toolbar_bottom_image"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text"
|
||||
app:srcCompat="@drawable/onboarding_toolbar_top" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/onboarding_card"
|
||||
style="@style/OnboardingCardLightWithPadding"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="12dp"
|
||||
android:gravity="center_vertical"
|
||||
android:lines="1"
|
||||
android:text="@string/onboarding_tracking_protection_header"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:drawableStart="@drawable/ic_onboarding_tracking_protection" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text"
|
||||
android:text="@string/onboarding_tracking_protection_description" />
|
||||
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/tracking_protection_standard_option"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/accessibility_min_height"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:checked="true"
|
||||
android:foreground="@drawable/rounded_ripple"
|
||||
android:gravity="top"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:theme="@style/Checkable.Colored"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/description_text"
|
||||
app:onboardingKey="@string/pref_key_tracking_protection_standard_option"
|
||||
app:onboardingKeyDescription="@string/onboarding_tracking_protection_standard_button_description_3"
|
||||
app:onboardingKeyTitle="@string/onboarding_tracking_protection_standard_button_2"
|
||||
tools:text="Standard" />
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/tracking_protection_strict_default"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="@dimen/accessibility_min_height"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:checked="false"
|
||||
android:foreground="@drawable/rounded_ripple"
|
||||
android:gravity="top"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:textColor="@color/primary_state_list_text_color"
|
||||
android:theme="@style/Checkable.Colored"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tracking_protection_standard_option"
|
||||
app:onboardingKey="@string/pref_key_tracking_protection_strict_default"
|
||||
app:onboardingKeyDescription="@string/onboarding_tracking_protection_strict_button_description_3"
|
||||
app:onboardingKeyTitle="@string/onboarding_tracking_protection_strict_option"
|
||||
tools:text="Strict" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,53 +0,0 @@
|
||||
/* 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.LayoutInflater
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.telemetry.glean.testing.GleanTestRule
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.databinding.OnboardingFinishBinding
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.onboarding.interactor.OnboardingInteractor
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class OnboardingFinishViewHolderTest {
|
||||
|
||||
@get:Rule
|
||||
val gleanTestRule = GleanTestRule(testContext)
|
||||
|
||||
private lateinit var binding: OnboardingFinishBinding
|
||||
private lateinit var interactor: OnboardingInteractor
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
binding = OnboardingFinishBinding.inflate(LayoutInflater.from(testContext))
|
||||
interactor = mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `call interactor on click`() {
|
||||
every { testContext.components.analytics } returns mockk(relaxed = true)
|
||||
OnboardingFinishViewHolder(binding.root, interactor)
|
||||
|
||||
binding.finishButton.performClick()
|
||||
verify { interactor.onFinishOnboarding(focusOnAddressBar = true) }
|
||||
// Check if the event was recorded
|
||||
assertNotNull(Onboarding.finish.testGetValue())
|
||||
assertEquals(1, Onboarding.finish.testGetValue()!!.size)
|
||||
assertNull(Onboarding.finish.testGetValue()!!.single().extra)
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/* 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.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.Navigation
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.telemetry.glean.testing.GleanTestRule
|
||||
import org.junit.After
|
||||
import org.junit.Assert
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.components.accounts.FenixFxAEntryPoint
|
||||
import org.mozilla.fenix.databinding.OnboardingManualSigninBinding
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.home.HomeFragmentDirections
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class OnboardingManualSignInViewHolderTest {
|
||||
|
||||
@get:Rule
|
||||
val gleanTestRule = GleanTestRule(testContext)
|
||||
|
||||
private lateinit var binding: OnboardingManualSigninBinding
|
||||
private lateinit var navController: NavController
|
||||
private lateinit var itemView: ViewGroup
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
binding = OnboardingManualSigninBinding.inflate(LayoutInflater.from(testContext))
|
||||
navController = mockk(relaxed = true)
|
||||
itemView = mockk(relaxed = true)
|
||||
|
||||
mockkStatic(Navigation::class)
|
||||
every { itemView.context } returns testContext
|
||||
every { Navigation.findNavController(binding.root) } returns navController
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
unmockkStatic(Navigation::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `navigate on click`() {
|
||||
every { testContext.components.analytics } returns mockk(relaxed = true)
|
||||
OnboardingManualSignInViewHolder(binding.root)
|
||||
binding.fxaSignInButton.performClick()
|
||||
|
||||
verify {
|
||||
navController.navigate(
|
||||
HomeFragmentDirections.actionGlobalTurnOnSync(
|
||||
entrypoint = FenixFxAEntryPoint.OnboardingManualSignIn,
|
||||
),
|
||||
)
|
||||
}
|
||||
// Check if the event was recorded
|
||||
Assert.assertNotNull(Onboarding.fxaManualSignin.testGetValue())
|
||||
assertEquals(1, Onboarding.fxaManualSignin.testGetValue()!!.size)
|
||||
Assert.assertNull(Onboarding.fxaManualSignin.testGetValue()!!.single().extra)
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/* 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.LayoutInflater
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.telemetry.glean.testing.GleanTestRule
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.OnboardingPrivacyNoticeBinding
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.onboarding.interactor.OnboardingInteractor
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class OnboardingPrivacyNoticeViewHolderTest {
|
||||
|
||||
@get:Rule
|
||||
val gleanTestRule = GleanTestRule(testContext)
|
||||
|
||||
private lateinit var binding: OnboardingPrivacyNoticeBinding
|
||||
private lateinit var interactor: OnboardingInteractor
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val context = ContextThemeWrapper(testContext, R.style.NormalTheme)
|
||||
binding = OnboardingPrivacyNoticeBinding.inflate(LayoutInflater.from(context))
|
||||
interactor = mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `call interactor on click`() {
|
||||
every { testContext.components.analytics } returns mockk(relaxed = true)
|
||||
OnboardingPrivacyNoticeViewHolder(binding.root, interactor)
|
||||
|
||||
binding.readButton.performClick()
|
||||
verify { interactor.onReadPrivacyNoticeClicked() }
|
||||
// Check if the event was recorded
|
||||
assertNotNull(Onboarding.privacyNotice.testGetValue())
|
||||
assertEquals(1, Onboarding.privacyNotice.testGetValue()!!.size)
|
||||
assertNull(Onboarding.privacyNotice.testGetValue()!!.single().extra)
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/* 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.LayoutInflater
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.databinding.OnboardingSectionHeaderBinding
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class OnboardingSectionHeaderViewHolderTest {
|
||||
|
||||
private lateinit var binding: OnboardingSectionHeaderBinding
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
binding = OnboardingSectionHeaderBinding.inflate(LayoutInflater.from(testContext))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bind text`() {
|
||||
val holder = OnboardingSectionHeaderViewHolder(binding.root)
|
||||
holder.bind { "Hello world" }
|
||||
|
||||
assertEquals(
|
||||
"Hello world",
|
||||
binding.sectionHeaderText.text,
|
||||
)
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/* 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.LayoutInflater
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.telemetry.glean.testing.GleanTestRule
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.GleanMetrics.Onboarding
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarPosition
|
||||
import org.mozilla.fenix.databinding.OnboardingToolbarPositionPickerBinding
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class OnboardingToolbarPositionPickerViewHolderTest {
|
||||
|
||||
@get:Rule
|
||||
val gleanTestRule = GleanTestRule(testContext)
|
||||
|
||||
private lateinit var binding: OnboardingToolbarPositionPickerBinding
|
||||
private lateinit var settings: Settings
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
binding = OnboardingToolbarPositionPickerBinding.inflate(LayoutInflater.from(testContext))
|
||||
settings = mockk(relaxed = true)
|
||||
every { testContext.components.settings } returns settings
|
||||
every { testContext.components.analytics } returns mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bottom illustration should select corresponding radio button`() {
|
||||
every { settings.toolbarPosition } returns ToolbarPosition.TOP
|
||||
OnboardingToolbarPositionPickerViewHolder(binding.root)
|
||||
assertTrue(binding.toolbarTopRadioButton.isChecked)
|
||||
assertFalse(binding.toolbarBottomRadioButton.isChecked)
|
||||
|
||||
binding.toolbarBottomImage.performClick()
|
||||
assertFalse(binding.toolbarTopRadioButton.isChecked)
|
||||
assertTrue(binding.toolbarBottomRadioButton.isChecked)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `top illustration should select corresponding radio button`() {
|
||||
every { settings.toolbarPosition } returns ToolbarPosition.BOTTOM
|
||||
OnboardingToolbarPositionPickerViewHolder(binding.root)
|
||||
assertFalse(binding.toolbarTopRadioButton.isChecked)
|
||||
assertTrue(binding.toolbarBottomRadioButton.isChecked)
|
||||
|
||||
binding.toolbarTopImage.performClick()
|
||||
assertTrue(binding.toolbarTopRadioButton.isChecked)
|
||||
assertFalse(binding.toolbarBottomRadioButton.isChecked)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN the top radio button is clicked THEN the proper event is recorded`() {
|
||||
every { settings.toolbarPosition } returns ToolbarPosition.BOTTOM
|
||||
OnboardingToolbarPositionPickerViewHolder(binding.root)
|
||||
|
||||
binding.toolbarTopImage.performClick()
|
||||
|
||||
assertNotNull(Onboarding.prefToggledToolbarPosition.testGetValue())
|
||||
assertEquals(
|
||||
OnboardingToolbarPositionPickerViewHolder.Companion.Position.TOP.name,
|
||||
Onboarding.prefToggledToolbarPosition.testGetValue()!!
|
||||
.last().extra?.get("position"),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN the bottom radio button is clicked THEN the proper event is recorded`() {
|
||||
every { settings.toolbarPosition } returns ToolbarPosition.TOP
|
||||
OnboardingToolbarPositionPickerViewHolder(binding.root)
|
||||
|
||||
binding.toolbarBottomImage.performClick()
|
||||
|
||||
assertNotNull(Onboarding.prefToggledToolbarPosition.testGetValue())
|
||||
assertEquals(
|
||||
OnboardingToolbarPositionPickerViewHolder.Companion.Position.BOTTOM.name,
|
||||
Onboarding.prefToggledToolbarPosition.testGetValue()!!
|
||||
.last().extra?.get("position"),
|
||||
)
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/* 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.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.OnboardingTrackingProtectionBinding
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class OnboardingTrackingProtectionViewHolderTest {
|
||||
@Test
|
||||
fun `GIVEN the TCP feature is public WHEN this ViewHolder is initialized THEN set an appropriate description`() {
|
||||
mockkStatic("org.mozilla.fenix.ext.ContextKt") {
|
||||
every { any<Context>().settings() } returns mockk(relaxed = true) {
|
||||
every { enabledTotalCookieProtectionCFR } returns true
|
||||
}
|
||||
val expectedDescription = testContext.getString(R.string.onboarding_tracking_protection_description)
|
||||
|
||||
val binding = OnboardingTrackingProtectionBinding.inflate(LayoutInflater.from(testContext))
|
||||
OnboardingTrackingProtectionViewHolder(binding.root)
|
||||
|
||||
assertEquals(expectedDescription, binding.descriptionText.text)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the TCP feature is not public WHEN this ViewHolder is initialized THEN set an appropriate description`() {
|
||||
mockkStatic("org.mozilla.fenix.ext.ContextKt") {
|
||||
every { any<Context>().settings() } returns mockk(relaxed = true) {
|
||||
every { enabledTotalCookieProtectionCFR } returns false
|
||||
}
|
||||
val expectedDescription = testContext.getString(
|
||||
R.string.onboarding_tracking_protection_description_old,
|
||||
testContext.getString(R.string.app_name),
|
||||
)
|
||||
|
||||
val binding = OnboardingTrackingProtectionBinding.inflate(LayoutInflater.from(testContext))
|
||||
OnboardingTrackingProtectionViewHolder(binding.root)
|
||||
|
||||
assertEquals(expectedDescription, binding.descriptionText.text)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/* 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.onboarding
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.RelaxedMockK
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.slot
|
||||
import io.mockk.unmockkObject
|
||||
import io.mockk.verify
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.telemetry.glean.testing.GleanTestRule
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.GleanMetrics.Events
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.onboarding.controller.DefaultOnboardingController
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class DefaultOnboardingControllerTest {
|
||||
|
||||
@get:Rule
|
||||
val gleanTestRule = GleanTestRule(testContext)
|
||||
|
||||
@RelaxedMockK
|
||||
private lateinit var activity: HomeActivity
|
||||
|
||||
@MockK(relaxUnitFun = true)
|
||||
private lateinit var navController: NavController
|
||||
|
||||
@RelaxedMockK
|
||||
private lateinit var onboarding: FenixOnboarding
|
||||
|
||||
private lateinit var controller: DefaultOnboardingController
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
|
||||
controller = DefaultOnboardingController(
|
||||
activity = activity,
|
||||
navController = navController,
|
||||
onboarding = onboarding,
|
||||
crashReporter = mockk(relaxed = true),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN focus on address bar is true WHEN onboarding is finished THEN navigate to home and log search bar tapped`() {
|
||||
assertNull(Events.searchBarTapped.testGetValue())
|
||||
|
||||
val focusOnAddressBar = true
|
||||
|
||||
controller.handleFinishOnboarding(focusOnAddressBar)
|
||||
|
||||
assertNotNull(Events.searchBarTapped.testGetValue())
|
||||
|
||||
val recordedEvents = Events.searchBarTapped.testGetValue()!!
|
||||
assertEquals(1, recordedEvents.size)
|
||||
assertEquals("HOME", recordedEvents.single().extra?.getValue("source"))
|
||||
|
||||
verify {
|
||||
onboarding.finish()
|
||||
|
||||
navController.navigate(
|
||||
OnboardingFragmentDirections.actionHome(focusOnAddressBar = focusOnAddressBar),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN focus on address bar is false WHEN onboarding is finished THEN navigate to home`() {
|
||||
assertNull(Events.searchBarTapped.testGetValue())
|
||||
|
||||
val focusOnAddressBar = false
|
||||
|
||||
controller.handleFinishOnboarding(focusOnAddressBar)
|
||||
|
||||
assertNull(Events.searchBarTapped.testGetValue())
|
||||
|
||||
verify {
|
||||
onboarding.finish()
|
||||
|
||||
navController.navigate(
|
||||
OnboardingFragmentDirections.actionHome(focusOnAddressBar = focusOnAddressBar),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN read privacy notice is clicked THEN open the privacy notice support page`() {
|
||||
mockkObject(SupportUtils)
|
||||
val urlCaptor = slot<String>()
|
||||
every { SupportUtils.createCustomTabIntent(any(), capture(urlCaptor)) } returns mockk()
|
||||
|
||||
controller.handleReadPrivacyNoticeClicked()
|
||||
|
||||
verify {
|
||||
activity.startActivity(
|
||||
any(),
|
||||
)
|
||||
}
|
||||
assertEquals(
|
||||
SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE),
|
||||
urlCaptor.captured,
|
||||
)
|
||||
unmockkObject(SupportUtils)
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
/* 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.onboarding
|
||||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.home.privatebrowsing.controller.PrivateBrowsingController
|
||||
import org.mozilla.fenix.home.toolbar.ToolbarController
|
||||
import org.mozilla.fenix.onboarding.controller.OnboardingController
|
||||
import org.mozilla.fenix.onboarding.interactor.DefaultOnboardingInteractor
|
||||
import org.mozilla.fenix.search.toolbar.SearchSelectorController
|
||||
import org.mozilla.fenix.search.toolbar.SearchSelectorMenu
|
||||
|
||||
class DefaultOnboardingInteractorTest {
|
||||
|
||||
private val controller: OnboardingController = mockk(relaxed = true)
|
||||
private val privateBrowsingController: PrivateBrowsingController = mockk(relaxed = true)
|
||||
private val searchSelectorController: SearchSelectorController = mockk(relaxed = true)
|
||||
private val toolbarController: ToolbarController = mockk(relaxed = true)
|
||||
|
||||
private lateinit var interactor: DefaultOnboardingInteractor
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
interactor = DefaultOnboardingInteractor(
|
||||
controller = controller,
|
||||
privateBrowsingController = privateBrowsingController,
|
||||
searchSelectorController = searchSelectorController,
|
||||
toolbarController = toolbarController,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN the onboarding is finished THEN forward to controller handler`() {
|
||||
val focusOnAddressBar = true
|
||||
interactor.onFinishOnboarding(focusOnAddressBar)
|
||||
verify { controller.handleFinishOnboarding(focusOnAddressBar) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN the privacy notice clicked THEN forward to controller handler`() {
|
||||
interactor.onReadPrivacyNoticeClicked()
|
||||
verify { controller.handleReadPrivacyNoticeClicked() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onMenuItemTapped() {
|
||||
val item = SearchSelectorMenu.Item.SearchSettings
|
||||
interactor.onMenuItemTapped(item)
|
||||
verify { searchSelectorController.handleMenuItemTapped(item) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onLearnMoreClicked() {
|
||||
interactor.onLearnMoreClicked()
|
||||
verify { privateBrowsingController.handleLearnMoreClicked() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onPrivateModeButtonClicked() {
|
||||
val newMode = BrowsingMode.Private
|
||||
val hasBeenOnboarded = true
|
||||
|
||||
interactor.onPrivateModeButtonClicked(newMode, hasBeenOnboarded)
|
||||
verify { privateBrowsingController.handlePrivateModeButtonClicked(newMode, hasBeenOnboarded) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onNavigateSearch() {
|
||||
interactor.onNavigateSearch()
|
||||
verify { toolbarController.handleNavigateSearch() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onPaste() {
|
||||
val clipboardText = "text"
|
||||
interactor.onPaste(clipboardText)
|
||||
verify { toolbarController.handlePaste(clipboardText) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onPasteAndGo() {
|
||||
val clipboardText = "text"
|
||||
interactor.onPasteAndGo(clipboardText)
|
||||
verify { toolbarController.handlePasteAndGo(clipboardText) }
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/* 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.onboarding
|
||||
|
||||
import android.content.Context
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.service.fxa.manager.FxaAccountManager
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
||||
class OnboardingAccountObserverTest {
|
||||
|
||||
private lateinit var context: Context
|
||||
private lateinit var accountManager: FxaAccountManager
|
||||
private lateinit var observer: OnboardingAccountObserver
|
||||
private lateinit var dispatchChanges: (mode: OnboardingState) -> Unit
|
||||
|
||||
private var dispatchChangesResult: OnboardingState? = null
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context = mockk(relaxed = true)
|
||||
accountManager = mockk(relaxed = true)
|
||||
|
||||
dispatchChangesResult = null
|
||||
dispatchChanges = {
|
||||
dispatchChangesResult = it
|
||||
}
|
||||
|
||||
every { context.components.backgroundServices.accountManager } returns accountManager
|
||||
|
||||
observer = spyk(
|
||||
OnboardingAccountObserver(
|
||||
context = context,
|
||||
dispatchChanges = dispatchChanges,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN account is authenticated THEN return signed in state`() {
|
||||
every { accountManager.authenticatedAccount() } returns mockk()
|
||||
|
||||
assertEquals(OnboardingState.SignedIn, observer.getOnboardingState())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN account is not authenticated THEN return signed out state`() {
|
||||
every { accountManager.authenticatedAccount() } returns null
|
||||
|
||||
assertEquals(OnboardingState.SignedOutNoAutoSignIn, observer.getOnboardingState())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN account observer receives a state change THEN emit state changes`() {
|
||||
observer.onAuthenticated(mockk(), mockk())
|
||||
observer.onAuthenticationProblems()
|
||||
observer.onLoggedOut()
|
||||
observer.onProfileUpdated(mockk())
|
||||
|
||||
verify(exactly = 4) { observer.emitChanges() }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue