For #26169 - MR Home Onboarding Dialog for upgrading users

This commit is contained in:
Gabriel Luong 2022-07-25 10:58:46 -04:00 committed by mergify[bot]
parent 2d99dbb1c5
commit 9a64acd4b6
14 changed files with 356 additions and 361 deletions

View File

@ -467,6 +467,7 @@ dependencies {
implementation Deps.androidx_constraintlayout
implementation Deps.androidx_coordinatorlayout
implementation Deps.google_accompanist_drawablepainter
implementation Deps.google_accompanist_insets
implementation Deps.sentry

View File

@ -30,7 +30,6 @@ import mozilla.components.support.ktx.android.view.showKeyboard
import mozilla.components.support.ktx.kotlin.isUrl
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.FeatureFlags
import org.mozilla.fenix.GleanMetrics.Collections
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.HomeScreen
@ -508,12 +507,10 @@ class DefaultSessionControlController(
}
override fun handleShowOnboardingDialog() {
if (FeatureFlags.showHomeOnboarding) {
navController.nav(
R.id.homeFragment,
HomeFragmentDirections.actionGlobalHomeOnboardingDialog()
)
}
navController.nav(
R.id.homeFragment,
HomeFragmentDirections.actionGlobalHomeOnboardingDialog()
)
}
override fun handleReadPrivacyNoticeClicked() {

View File

@ -171,13 +171,6 @@ private fun AppState.toAdapterList(settings: Settings): List<AdapterItem> = when
is Mode.Onboarding -> onboardingAdapterItems(mode.state)
}
@VisibleForTesting
internal fun AppState.shouldShowHomeOnboardingDialog(settings: Settings): Boolean {
val isAnySectionsVisible = recentTabs.isNotEmpty() || recentBookmarks.isNotEmpty() ||
recentHistory.isNotEmpty() || pocketStories.isNotEmpty()
return isAnySectionsVisible && !settings.hasShownHomeOnboardingDialog
}
private fun collectionTabItems(collection: TabCollection) =
collection.tabs.mapIndexed { index, tab ->
AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
@ -211,7 +204,7 @@ class SessionControlView(
}
fun update(state: AppState, shouldReportMetrics: Boolean = false) {
if (state.shouldShowHomeOnboardingDialog(view.context.settings())) {
if (view.context.settings().showHomeOnboardingDialog) {
interactor.showOnboardingDialog()
}

View File

@ -4,48 +4,72 @@
package org.mozilla.fenix.onboarding
import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.DialogFragment
import androidx.navigation.fragment.findNavController
import com.google.accompanist.insets.ProvideWindowInsets
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentOnboardingHomeDialogBinding
import org.mozilla.fenix.components.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.onboarding.view.Onboarding
import org.mozilla.fenix.theme.FirefoxTheme
/**
* Dialog displayed once when one or multiples of these sections are shown in the home screen
* recentTabs,recentBookmarks,historyMetadata or pocketArticles.
* Dialog displaying a welcome and sync sign in onboarding.
*/
class HomeOnboardingDialogFragment : DialogFragment() {
@SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.HomeOnboardingDialogStyle)
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
override fun onDestroy() {
super.onDestroy()
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_onboarding_home_dialog, container, false)
): View = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentOnboardingHomeDialogBinding.bind(view)
setContent {
ProvideWindowInsets {
FirefoxTheme {
val account =
components.backgroundServices.syncStore.observeAsComposableState { state -> state.account }
val appName = requireContext().getString(R.string.app_name)
binding.welcomeTitle.text =
requireContext().getString(R.string.onboarding_home_screen_title_3, appName)
binding.homeTitle.text = requireContext().getString(
R.string.onboarding_home_screen_section_home_title_3,
appName
)
binding.finishButton.setOnClickListener {
context?.settings()?.let { settings ->
settings.hasShownHomeOnboardingDialog = true
Onboarding(
isSyncSignIn = account.value != null,
onDismiss = ::onDismiss,
onSignInButtonClick = {
findNavController().nav(
R.id.homeOnboardingDialogFragment,
HomeOnboardingDialogFragmentDirections.actionGlobalTurnOnSync()
)
onDismiss()
},
)
}
}
dismiss()
}
}
private fun onDismiss() {
context?.settings()?.showHomeOnboardingDialog = false
dismiss()
}
}

View File

@ -0,0 +1,279 @@
/* 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.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.accompanist.insets.navigationBarsPadding
import com.google.accompanist.insets.statusBarsPadding
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.button.PrimaryButton
import org.mozilla.fenix.compose.button.SecondaryButton
import org.mozilla.fenix.theme.FirefoxTheme
/**
* Enum that represents the onboarding screen that is displayed.
*/
private enum class OnboardingState {
Welcome,
SyncSignIn
}
/**
* A screen for displaying a welcome and sync sign in onboarding.
*
* @param isSyncSignIn Whether or not the user is signed into their Firefox Sync account.
* @param onDismiss Invoked when the user clicks on the close or "Skip" button.
* @param onSignInButtonClick Invoked when the user clicks on the "Sign In" button
*/
@Composable
fun Onboarding(
isSyncSignIn: Boolean,
onDismiss: () -> Unit,
onSignInButtonClick: () -> Unit,
) {
var onboardingState by remember { mutableStateOf(OnboardingState.Welcome) }
Column(
modifier = Modifier
.background(FirefoxTheme.colors.layer1)
.fillMaxSize()
.padding(bottom = 32.dp)
.statusBarsPadding()
.navigationBarsPadding()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
) {
IconButton(onClick = onDismiss) {
Icon(
painter = painterResource(id = R.drawable.mozac_ic_close),
contentDescription = null,
tint = FirefoxTheme.colors.iconPrimary,
)
}
}
if (onboardingState == OnboardingState.Welcome) {
OnboardingWelcomeContent()
OnboardingWelcomeBottomContent(
onboardingState = onboardingState,
isSyncSignIn = isSyncSignIn,
onGetStartedButtonClick = {
if (isSyncSignIn) {
onDismiss()
} else {
onboardingState = OnboardingState.SyncSignIn
}
},
)
} else if (onboardingState == OnboardingState.SyncSignIn) {
OnboardingSyncSignInContent()
OnboardingSyncSignInBottomContent(
onboardingState = onboardingState,
onSignInButtonClick = onSignInButtonClick,
onSkipButtonClick = onDismiss,
)
}
}
}
@Composable
private fun OnboardingWelcomeBottomContent(
onboardingState: OnboardingState,
isSyncSignIn: Boolean,
onGetStartedButtonClick: () -> Unit
) {
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
PrimaryButton(
text = stringResource(id = R.string.onboarding_home_get_started_button),
onClick = onGetStartedButtonClick,
)
Spacer(modifier = Modifier.height(32.dp))
if (isSyncSignIn) {
Spacer(modifier = Modifier.height(6.dp))
} else {
Indicators(onboardingState = onboardingState)
}
}
}
@Composable
private fun OnboardingWelcomeContent() {
Column(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(id = R.drawable.ic_firefox),
contentDescription = null,
modifier = Modifier.size(109.dp),
)
Spacer(modifier = Modifier.height(32.dp))
Text(
text = stringResource(id = R.string.onboarding_home_welcome_title),
color = FirefoxTheme.colors.textPrimary,
textAlign = TextAlign.Center,
style = FirefoxTheme.typography.headline5,
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(id = R.string.onboarding_home_welcome_description),
color = FirefoxTheme.colors.textSecondary,
textAlign = TextAlign.Center,
style = FirefoxTheme.typography.body2,
)
}
}
@Composable
private fun OnboardingSyncSignInContent() {
Column(
modifier = Modifier.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(id = R.drawable.ic_scan),
contentDescription = null,
modifier = Modifier.size(320.dp, 166.dp),
contentScale = ContentScale.FillBounds,
)
Spacer(modifier = Modifier.height(32.dp))
Text(
text = stringResource(id = R.string.onboarding_home_sync_title),
color = FirefoxTheme.colors.textPrimary,
textAlign = TextAlign.Center,
style = FirefoxTheme.typography.headline5,
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(id = R.string.onboarding_home_sync_description),
color = FirefoxTheme.colors.textSecondary,
textAlign = TextAlign.Center,
style = FirefoxTheme.typography.body2,
)
}
}
@Composable
private fun OnboardingSyncSignInBottomContent(
onboardingState: OnboardingState,
onSignInButtonClick: () -> Unit,
onSkipButtonClick: () -> Unit,
) {
Column(modifier = Modifier.padding(horizontal = 16.dp)) {
PrimaryButton(
text = stringResource(id = R.string.onboarding_home_sign_in_button),
onClick = onSignInButtonClick,
)
Spacer(modifier = Modifier.height(8.dp))
SecondaryButton(
text = stringResource(id = R.string.onboarding_home_skip_button),
onClick = onSkipButtonClick,
)
Spacer(modifier = Modifier.height(24.dp))
Indicators(onboardingState = onboardingState)
}
}
@Composable
private fun Indicators(onboardingState: OnboardingState) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
Indicator(
color = if (onboardingState == OnboardingState.Welcome) {
FirefoxTheme.colors.indicatorActive
} else {
FirefoxTheme.colors.indicatorInactive
}
)
Spacer(modifier = Modifier.width(8.dp))
Indicator(
color = if (onboardingState == OnboardingState.SyncSignIn) {
FirefoxTheme.colors.indicatorActive
} else {
FirefoxTheme.colors.indicatorInactive
}
)
}
}
@Composable
private fun Indicator(color: Color) {
Box(
modifier = Modifier
.size(6.dp)
.clip(CircleShape)
.background(color),
)
}
@Composable
@Preview
private fun OnboardingPreview() {
FirefoxTheme {
Onboarding(
isSyncSignIn = false,
onDismiss = {},
onSignInButtonClick = {},
)
}
}

View File

@ -791,14 +791,6 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = false
)
/**
* Indicates if the home onboarding dialog has already shown before.
*/
var hasShownHomeOnboardingDialog by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_has_shown_home_onboarding),
default = false
)
fun incrementVisitedInstallableCount() = pwaInstallableVisitCount.increment()
@VisibleForTesting(otherwise = PRIVATE)

View File

@ -1,219 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!-- 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.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:fitsSystemWindows="true"
android:fillViewport="true"
android:background="?scrim"
android:scrollbars="none">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/dialog_background"
android:layout_marginTop="16dp" >
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/firefox_logo"
android:layout_width="59dp"
android:layout_height="60dp"
android:layout_marginTop="30dp"
android:importantForAccessibility="no"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_firefox" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/welcome_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginHorizontal="@dimen/oboarding_home_dialog_margin_horizontal"
app:lineHeight="24sp"
android:textAppearance="@style/Header20TextStyle"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/firefox_logo" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:layout_marginHorizontal="@dimen/oboarding_home_dialog_margin_horizontal"
app:lineHeight="24sp"
android:textAppearance="@style/Body14TextStyle"
android:text="@string/onboarding_home_screen_description_2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/welcome_title" />
<ImageView
android:id="@+id/home_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="26dp"
android:layout_marginHorizontal="20dp"
android:importantForAccessibility="no"
app:layout_constraintEnd_toStartOf="@id/home_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/description"
app:srcCompat="@drawable/mozac_ic_home"
app:tint="?attr/textPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/home_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal"
android:layout_marginTop="26dp"
app:lineHeight="20sp"
android:textAppearance="@style/Header14TextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/home_icon"
app:layout_constraintTop_toBottomOf="@id/description" />
<View
android:id="@+id/home_placeholder"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginHorizontal="20dp"
android:importantForAccessibility="no"
app:layout_constraintEnd_toStartOf="@id/home_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/home_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/home_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal"
app:lineHeight="20sp"
android:text="@string/onboarding_home_screen_section_home_description_2"
android:textAppearance="@style/Body14TextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/home_placeholder"
app:layout_constraintTop_toBottomOf="@+id/home_title" />
<ImageView
android:id="@+id/cleaner_tab_tray_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="26dp"
android:layout_marginHorizontal="20dp"
android:importantForAccessibility="no"
app:layout_constraintEnd_toStartOf="@id/cleaner_tab_tray_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/home_description"
app:srcCompat="@drawable/ic_multiple_tabs"
app:tint="?attr/textPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/cleaner_tab_tray_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal"
android:layout_marginTop="26dp"
app:lineHeight="20dp"
android:text="@string/onboarding_home_screen_section_cleaner_tab_tray_title_2"
android:textAppearance="@style/Header14TextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/cleaner_tab_tray_icon"
app:layout_constraintTop_toBottomOf="@id/home_description" />
<View
android:id="@+id/cleaner_tab_tray_placeholder"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginHorizontal="20dp"
android:importantForAccessibility="no"
app:layout_constraintEnd_toStartOf="@id/cleaner_tab_tray_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cleaner_tab_tray_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/cleaner_tab_tray_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal"
app:lineHeight="20sp"
android:text="@string/onboarding_home_screen_section_cleaner_tab_tray_description_2"
android:textAppearance="@style/Body14TextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/cleaner_tab_tray_placeholder"
app:layout_constraintTop_toBottomOf="@+id/cleaner_tab_tray_title" />
<ImageView
android:id="@+id/useful_history_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginTop="26dp"
android:layout_marginHorizontal="20dp"
android:importantForAccessibility="no"
app:layout_constraintEnd_toStartOf="@id/useful_history_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cleaner_tab_tray_description"
app:srcCompat="@drawable/ic_history"
app:tint="?attr/textPrimary" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/useful_history_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal"
android:layout_marginTop="26dp"
app:lineHeight="20sp"
android:text="@string/onboarding_home_screen_section_useful_history_title_2"
android:textAppearance="@style/Header14TextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/useful_history_icon"
app:layout_constraintTop_toBottomOf="@id/cleaner_tab_tray_description" />
<View
android:id="@+id/useful_history_placeholder"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginHorizontal="20dp"
android:importantForAccessibility="no"
app:layout_constraintEnd_toStartOf="@id/useful_history_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/useful_history_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/useful_history_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:layout_marginEnd="@dimen/oboarding_home_dialog_margin_horizontal"
app:lineHeight="20sp"
android:text="@string/onboarding_home_screen_section_useful_history_description_2"
android:textAppearance="@style/Body14TextStyle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/useful_history_placeholder"
app:layout_constraintTop_toBottomOf="@+id/useful_history_title" />
<Button
android:id="@+id/finish_button"
style="@style/PositiveButton"
android:layout_marginHorizontal="64dp"
android:layout_marginTop="42dp"
android:background="@drawable/onboarding_padded_background"
android:text="@string/onboarding_finish"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/useful_history_description" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -187,8 +187,7 @@
</fragment>
<dialog
android:id="@+id/homeOnboardingDialogFragment"
android:name="org.mozilla.fenix.onboarding.HomeOnboardingDialogFragment"
tools:layout="@layout/fragment_onboarding_home_dialog"/>
android:name="org.mozilla.fenix.onboarding.HomeOnboardingDialogFragment" />
<dialog
android:id="@+id/searchDialogFragment"

View File

@ -134,9 +134,6 @@
<dimen name="onboarding_dual_pane_radio_button_translation_x">-7dp</dimen>
<dimen name="onboarding_dual_pane_radio_button_translation_y">-8dp</dimen>
<!--Onboarding home dialog-->
<dimen name="oboarding_home_dialog_margin_horizontal">24dp</dimen>
<!-- Addon Details Fragment -->
<dimen name="addon_details_rating_view_margin_start">6dp</dimen>
<dimen name="addon_details_rating_view_margin_end">6dp</dimen>

View File

@ -66,7 +66,6 @@
<string name="pref_key_last_cfr_shown_time" translatable="false">pref_key_last_cfr_shown_time</string>
<string name="pref_key_should_show_default_browser_notification" translatable="false">pref_key_should_show_default_browser_notification</string>
<string name="pref_key_is_first_run" translatable="false">pref_key_is_first_run</string>
<string name="pref_key_has_shown_home_onboarding" translatable="false">pref_key_has_shown_home_onboarding</string>
<string name="pref_key_home_blocklist">pref_key_home_blocklist</string>
<!-- Data Choices -->

View File

@ -246,24 +246,37 @@
<!-- Home onboarding -->
<!-- Onboarding home screen dialog title text. The first parameter is the name of the application.-->
<string name="onboarding_home_screen_title_3">Whats new in %1$s</string>
<string name="onboarding_home_screen_title_3" moz:RemovedIn="106" tools:ignore="UnusedResources">Whats new in %1$s</string>
<!-- Onboarding home screen dialog description text. -->
<string name="onboarding_home_screen_description_2">Its now easier to pick back up where you left off.</string>
<string name="onboarding_home_screen_description_2" moz:RemovedIn="106" tools:ignore="UnusedResources">Its now easier to pick back up where you left off.</string>
<!-- Onboarding home screen dialog title text for the home section. The first parameter is the name of the application.-->
<string name="onboarding_home_screen_section_home_title_3">Personalized %1$s homepage</string>
<string name="onboarding_home_screen_section_home_title_3" moz:RemovedIn="106" tools:ignore="UnusedResources">Personalized %1$s homepage</string>
<!-- Onboarding home screen dialog description text for the home section. -->
<string name="onboarding_home_screen_section_home_description_2">Jump to your open tabs, bookmarks, and browsing history.</string>
<string name="onboarding_home_screen_section_home_description_2" moz:RemovedIn="106" tools:ignore="UnusedResources">Jump to your open tabs, bookmarks, and browsing history.</string>
<!-- Onboarding home screen dialog description text for the tab tray section. -->
<string name="onboarding_home_screen_section_cleaner_tab_tray_title_2">Clean, organized tabs</string>
<string name="onboarding_home_screen_section_cleaner_tab_tray_title_2" moz:RemovedIn="106" tools:ignore="UnusedResources">Clean, organized tabs</string>
<!-- Onboarding home screen dialog description text for the tab tray section. -->
<string name="onboarding_home_screen_section_cleaner_tab_tray_description_2">Clear away tab clutter with improved layout and auto-closing tabs. </string>
<string name="onboarding_home_screen_section_cleaner_tab_tray_description_2" moz:RemovedIn="106" tools:ignore="UnusedResources">Clear away tab clutter with improved layout and auto-closing tabs. </string>
<!-- Onboarding home screen dialog description text for the history section. -->
<string name="onboarding_home_screen_section_useful_history_title_2">Recent searches</string>
<string name="onboarding_home_screen_section_useful_history_title_2" moz:RemovedIn="106" tools:ignore="UnusedResources">Recent searches</string>
<!-- Onboarding home screen dialog description text for the history section. -->
<string name="onboarding_home_screen_section_useful_history_description_2">Revisit your latest searches from your homepage and tabs.</string>
<string name="onboarding_home_screen_section_useful_history_description_2" moz:RemovedIn="106" tools:ignore="UnusedResources">Revisit your latest searches from your homepage and tabs.</string>
<!-- Onboarding home screen popup dialog, shown on top of the Jump back in section. Firefox is intentionally hardcoded. -->
<string name="onboarding_home_screen_jump_back_contextual_hint" tools:ignore="UnusedResources">Your personalized Firefox homepage now makes it easier to pick up where you left off. Find your recent tabs, bookmarks, and search results.</string>
<string name="onboarding_home_screen_jump_back_contextual_hint">Your personalized Firefox homepage now makes it easier to pick up where you left off. Find your recent tabs, bookmarks, and search results.</string>
<!-- Home onboarding dialog welcome screen title text. -->
<string name="onboarding_home_welcome_title">Welcome to an independent internet</string>
<!-- Home onboarding dialog welcome screen description text. -->
<string name="onboarding_home_welcome_description">Pick up where you left off with tabs from other devices now on your homepage.</string>
<!-- Home onboarding dialog sign into sync screen title text. -->
<string name="onboarding_home_sync_title">Switching screens is easier than ever</string>
<!-- Home onboarding dialog sign into sync screen description text. -->
<string name="onboarding_home_sync_description">Pick up where you left off with tabs from other devices now on your homepage.</string>
<!-- Text for the button to continue the onboarding on the home onboarding dialog. -->
<string name="onboarding_home_get_started_button">Get started</string>
<!-- Text for the button to navigate to the sync sign in screen on the home onboarding dialog. -->
<string name="onboarding_home_sign_in_button">Sign in</string>
<!-- Text for the button to skip the onboarding on the home onboarding dialog. -->
<string name="onboarding_home_skip_button">Skip</string>
<!-- Search Widget -->
<!-- Content description for searching with a widget. The first parameter is the name of the application.-->

View File

@ -420,7 +420,7 @@
<item name="android:fontWeight" tools:ignore="NewApi">@integer/font_weight_medium</item>
</style>
<style name="Header20TextStyle" parent="TextAppearance.MaterialComponents.Body1">
<style name="Header20TextStyle" parent="TextAppearance.MaterialComponents.Body1" tools:ignore="UnusedResources">
<item name="android:textColor">?attr/textPrimary</item>
<item name="android:textSize">20sp</item>
<item name="android:textFontWeight" tools:ignore="NewApi">@integer/font_weight_medium</item>

View File

@ -14,7 +14,6 @@ import mozilla.components.service.pocket.PocketStory
import mozilla.components.service.pocket.PocketStory.PocketRecommendedStory
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
@ -23,94 +22,12 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.gleanplumb.Message
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.recentbookmarks.RecentBookmark
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryGroup
import org.mozilla.fenix.utils.Settings
@RunWith(FenixRobolectricTestRunner::class)
class SessionControlViewTest {
@Test
fun `GIVEN recent Bookmarks WHEN calling shouldShowHomeOnboardingDialog THEN show the dialog `() {
val recentBookmarks = listOf(RecentBookmark())
val settings: Settings = mockk()
every { settings.hasShownHomeOnboardingDialog } returns false
val state = AppState(recentBookmarks = recentBookmarks)
assertTrue(state.shouldShowHomeOnboardingDialog(settings))
}
@Test
fun `GIVEN recentTabs WHEN calling shouldShowHomeOnboardingDialog THEN show the dialog `() {
val recentTabs = listOf<RecentTab>(mockk())
val settings: Settings = mockk()
every { settings.hasShownHomeOnboardingDialog } returns false
val state = AppState(recentTabs = recentTabs)
assertTrue(state.shouldShowHomeOnboardingDialog(settings))
}
@Test
fun `GIVEN historyMetadata WHEN calling shouldShowHomeOnboardingDialog THEN show the dialog `() {
val historyMetadata = listOf(RecentHistoryGroup("title", emptyList()))
val settings: Settings = mockk()
every { settings.hasShownHomeOnboardingDialog } returns false
val state = AppState(recentHistory = historyMetadata)
assertTrue(state.shouldShowHomeOnboardingDialog(settings))
}
@Test
fun `GIVEN pocketArticles WHEN calling shouldShowHomeOnboardingDialog THEN show the dialog `() {
val pocketStories = listOf(PocketRecommendedStory("", "", "", "", "", 0, 0))
val settings: Settings = mockk()
every { settings.hasShownHomeOnboardingDialog } returns false
val state = AppState(pocketStories = pocketStories)
assertTrue(state.shouldShowHomeOnboardingDialog(settings))
}
@Test
fun `GIVEN the home onboading dialog has been shown before WHEN calling shouldShowHomeOnboardingDialog THEN DO NOT showthe dialog `() {
val pocketStories = listOf(PocketRecommendedStory("", "", "", "", "", 0, 0))
val settings: Settings = mockk()
every { settings.hasShownHomeOnboardingDialog } returns true
val state = AppState(pocketStories = pocketStories)
assertFalse(state.shouldShowHomeOnboardingDialog(settings))
}
@Test
fun `GIVENs updates WHEN sections recentTabs, recentBookmarks, historyMetadata or pocketArticles are available THEN show the dialog`() {
every { testContext.components.settings } returns mockk(relaxed = true)
val interactor = mockk<SessionControlInteractor>(relaxed = true)
val view = RecyclerView(testContext)
val controller = SessionControlView(
view,
mockk(relaxed = true),
interactor
)
val recentTabs = listOf<RecentTab>(mockk(relaxed = true))
val state = AppState(recentTabs = recentTabs)
controller.update(state)
verify {
interactor.showOnboardingDialog()
}
}
@Test
fun `GIVENs updates WHEN sections recentTabs, recentBookmarks, historyMetadata or pocketArticles are NOT available THEN DO NOT show the dialog`() {
every { testContext.components.settings } returns mockk(relaxed = true)

View File

@ -40,6 +40,7 @@ object Versions {
const val androidx_datastore = "1.0.0"
const val google_material = "1.2.1"
const val accompanist_drawablepainter = "0.23.1"
const val accompanist_insets = "0.23.1"
const val mozilla_android_components = AndroidComponents.VERSION
@ -212,6 +213,8 @@ object Deps {
const val google_material = "com.google.android.material:material:${Versions.google_material}"
const val google_accompanist_drawablepainter =
"com.google.accompanist:accompanist-drawablepainter:${Versions.accompanist_drawablepainter}"
const val google_accompanist_insets =
"com.google.accompanist:accompanist-insets:${Versions.accompanist_drawablepainter}"
const val protobuf_javalite = "com.google.protobuf:protobuf-javalite:${Versions.protobuf}"
const val protobuf_compiler = "com.google.protobuf:protoc:${Versions.protobuf}"