Bug 1951950 - Add the setup checklist UI to the homepage view holder, set the enabled/disabled state and the list of items to display with the Nimbus configuration. r=android-reviewers,gmalekpour
Differential Revision: https://phabricator.services.mozilla.com/D243426
This commit is contained in:
@@ -14,8 +14,6 @@ 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.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -32,6 +30,7 @@ import mozilla.components.compose.base.annotation.FlexibleWindowLightDarkPreview
|
||||
import mozilla.components.compose.base.theme.AcornTheme
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.appstate.setup.checklist.ChecklistItem
|
||||
import org.mozilla.fenix.home.sessioncontrol.SetupChecklistInteractor
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
|
||||
private const val ROTATE_180 = 180F
|
||||
@@ -39,25 +38,26 @@ private const val ROTATE_180 = 180F
|
||||
/**
|
||||
* Renders a checklist for onboarding users.
|
||||
*
|
||||
* @param interactor the interactor to handle user actions.
|
||||
* @param checkListItems The list of [ChecklistItem] displayed in setup checklist.
|
||||
* @param onChecklistItemClicked Gets invoked when the user clicks a check list item.
|
||||
*/
|
||||
@Composable
|
||||
fun CheckListView(
|
||||
interactor: SetupChecklistInteractor,
|
||||
checkListItems: List<ChecklistItem>,
|
||||
onChecklistItemClicked: (ChecklistItem) -> Unit,
|
||||
) {
|
||||
LazyColumn {
|
||||
itemsIndexed(checkListItems) { index, item ->
|
||||
Column {
|
||||
checkListItems.forEachIndexed { index, item ->
|
||||
when (item) {
|
||||
is ChecklistItem.Group -> GroupWithTasks(
|
||||
group = item,
|
||||
onChecklistItemClicked = onChecklistItemClicked,
|
||||
onChecklistItemClicked = { interactor.onChecklistItemClicked(item) },
|
||||
// No divider for the last group, in case it is the last element
|
||||
// in the parent composable.
|
||||
addDivider = index != checkListItems.size - 1,
|
||||
)
|
||||
is ChecklistItem.Task -> Task(item, onChecklistItemClicked)
|
||||
|
||||
is ChecklistItem.Task -> Task(item) { interactor.onChecklistItemClicked(item) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ private fun Task(
|
||||
}
|
||||
|
||||
Text(
|
||||
text = task.title,
|
||||
text = stringResource(task.title),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.semantics { heading() },
|
||||
@@ -142,7 +142,7 @@ private fun Group(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Text(
|
||||
text = group.title,
|
||||
text = stringResource(group.title),
|
||||
style = FirefoxTheme.typography.subtitle1,
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
modifier = Modifier.semantics { heading() },
|
||||
@@ -178,32 +178,35 @@ private fun TasksChecklistPreview() {
|
||||
val tasks = listOf(
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.EXPLORE_EXTENSION,
|
||||
title = "First task",
|
||||
title = R.string.setup_checklist_task_explore_extensions,
|
||||
icon = R.drawable.ic_addons_extensions,
|
||||
isCompleted = true,
|
||||
),
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.INSTALL_SEARCH_WIDGET,
|
||||
title = "Second task",
|
||||
title = R.string.setup_checklist_task_search_widget,
|
||||
icon = R.drawable.ic_search,
|
||||
isCompleted = false,
|
||||
),
|
||||
)
|
||||
|
||||
val group1 = ChecklistItem.Group(
|
||||
title = "First group",
|
||||
title = R.string.setup_checklist_group_essentials,
|
||||
tasks = tasks,
|
||||
isExpanded = true,
|
||||
)
|
||||
val group2 = ChecklistItem.Group(
|
||||
title = "Second group",
|
||||
title = R.string.setup_checklist_group_customize,
|
||||
tasks = tasks,
|
||||
isExpanded = false,
|
||||
)
|
||||
|
||||
CheckListView(
|
||||
interactor = object : SetupChecklistInteractor {
|
||||
override fun onChecklistItemClicked(item: ChecklistItem) { /* no op */ }
|
||||
override fun onRemoveChecklistButtonClicked() { /* no op */ }
|
||||
},
|
||||
checkListItems = listOf(group1, group2),
|
||||
onChecklistItemClicked = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.mozilla.fenix.components.appstate.setup.checklist.ChecklistItem
|
||||
import org.mozilla.fenix.components.appstate.setup.checklist.Progress
|
||||
import org.mozilla.fenix.components.appstate.setup.checklist.SetupChecklistState
|
||||
import org.mozilla.fenix.compose.button.PrimaryButton
|
||||
import org.mozilla.fenix.home.sessioncontrol.SetupChecklistInteractor
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
|
||||
private val elevation = AcornLayout.AcornElevation.xLarge
|
||||
@@ -43,22 +44,21 @@ private val shapeChecklist = RoundedCornerShape(size = AcornLayout.AcornCorner.l
|
||||
* The Setup checklist displayed on homepage that contains onboarding tasks.
|
||||
*
|
||||
* @param setupChecklistState The [SetupChecklistState] used to manage the state of feature.
|
||||
* @param interactor The [SetupChecklistInteractor] used to handle user interactions.
|
||||
* @param title The checklist's title.
|
||||
* @param subtitle The checklist's subtitle.
|
||||
* @param labelRemoveChecklistButton The label of the checklist's button to remove it.
|
||||
* @param onChecklistItemClicked Gets invoked when the user clicks a check list item.
|
||||
* @param onRemoveChecklistButtonClicked Invoked when the remove button is clicked.
|
||||
*/
|
||||
@Composable
|
||||
fun SetupChecklist(
|
||||
setupChecklistState: SetupChecklistState,
|
||||
interactor: SetupChecklistInteractor,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
labelRemoveChecklistButton: String,
|
||||
onChecklistItemClicked: (ChecklistItem) -> Unit,
|
||||
onRemoveChecklistButtonClicked: () -> Unit,
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
shape = shapeChecklist,
|
||||
backgroundColor = FirefoxTheme.colors.layer1,
|
||||
elevation = elevation,
|
||||
@@ -67,14 +67,20 @@ fun SetupChecklist(
|
||||
verticalArrangement = Arrangement.spacedBy(0.dp, Alignment.CenterVertically),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Header(title, subtitle, setupChecklistState.progress)
|
||||
Header(title = title, subtitle = subtitle, progress = setupChecklistState.progress)
|
||||
|
||||
CheckListView(setupChecklistState.checklistItems, onChecklistItemClicked)
|
||||
CheckListView(
|
||||
interactor = interactor,
|
||||
checkListItems = setupChecklistState.checklistItems,
|
||||
)
|
||||
|
||||
if (setupChecklistState.progress.allTasksCompleted()) {
|
||||
Divider()
|
||||
|
||||
RemoveChecklistButton(labelRemoveChecklistButton, onRemoveChecklistButtonClicked)
|
||||
RemoveChecklistButton(
|
||||
interactor = interactor,
|
||||
labelRemoveChecklistButton = labelRemoveChecklistButton,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,14 +96,8 @@ private fun Header(
|
||||
progress: Progress,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 12.dp,
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
12.dp,
|
||||
Alignment.Top,
|
||||
),
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Text(
|
||||
@@ -121,9 +121,9 @@ private fun Header(
|
||||
* The button that will remove the setup checklist from homepage.
|
||||
*/
|
||||
@Composable
|
||||
fun RemoveChecklistButton(
|
||||
private fun RemoveChecklistButton(
|
||||
interactor: SetupChecklistInteractor,
|
||||
labelRemoveChecklistButton: String,
|
||||
onRemoveChecklistButtonClicked: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(24.dp),
|
||||
@@ -136,9 +136,8 @@ fun RemoveChecklistButton(
|
||||
) {
|
||||
PrimaryButton(
|
||||
text = labelRemoveChecklistButton,
|
||||
modifier = Modifier
|
||||
.width(width = FirefoxTheme.layout.size.maxWidth.small),
|
||||
onClick = onRemoveChecklistButtonClicked,
|
||||
modifier = Modifier.width(width = FirefoxTheme.layout.size.maxWidth.small),
|
||||
onClick = { interactor.onRemoveChecklistButtonClicked() },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -154,57 +153,50 @@ private class SetupChecklistPreviewParameterProvider :
|
||||
}
|
||||
|
||||
private fun createPreviewTasks() = listOf(
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SET_AS_DEFAULT,
|
||||
title = "Set as default browser",
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = false,
|
||||
),
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.EXPLORE_EXTENSION,
|
||||
title = "Explore extensions",
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = false,
|
||||
),
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SIGN_IN,
|
||||
title = "Sign in to your account",
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = true,
|
||||
),
|
||||
setAsDefaultTaskPreview(),
|
||||
webExtensionTaskPreview(),
|
||||
signInTaskPreview(),
|
||||
)
|
||||
|
||||
private fun setAsDefaultTaskPreview() = ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SET_AS_DEFAULT,
|
||||
title = R.string.setup_checklist_task_default_browser,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = false,
|
||||
)
|
||||
|
||||
private fun webExtensionTaskPreview() = ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.EXPLORE_EXTENSION,
|
||||
title = R.string.setup_checklist_task_explore_extensions,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = false,
|
||||
)
|
||||
|
||||
private fun signInTaskPreview() = ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SIGN_IN,
|
||||
title = R.string.setup_checklist_task_account_sync,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = true,
|
||||
)
|
||||
|
||||
private fun createPreviewGroups() = listOf(
|
||||
ChecklistItem.Group(
|
||||
title = "First group",
|
||||
tasks = listOf(
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SET_AS_DEFAULT,
|
||||
title = "Set as default browser",
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = false,
|
||||
),
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SIGN_IN,
|
||||
title = "Sign in to your account",
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = true,
|
||||
),
|
||||
),
|
||||
title = R.string.setup_checklist_group_essentials,
|
||||
tasks = listOf(setAsDefaultTaskPreview(), signInTaskPreview()),
|
||||
isExpanded = true,
|
||||
),
|
||||
ChecklistItem.Group(
|
||||
title = "Second group",
|
||||
title = R.string.setup_checklist_group_customize,
|
||||
tasks = listOf(
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SELECT_THEME,
|
||||
title = "Select a theme",
|
||||
title = R.string.setup_checklist_task_toolbar_selection,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = false,
|
||||
),
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.CHANGE_TOOLBAR_PLACEMENT,
|
||||
title = "Choose toolbar placement",
|
||||
title = R.string.setup_checklist_task_theme_selection,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = false,
|
||||
),
|
||||
@@ -212,17 +204,17 @@ private fun createPreviewGroups() = listOf(
|
||||
isExpanded = false,
|
||||
),
|
||||
ChecklistItem.Group(
|
||||
title = "Second group",
|
||||
title = R.string.setup_checklist_group_helpful_tools,
|
||||
tasks = listOf(
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.INSTALL_SEARCH_WIDGET,
|
||||
title = "Install search widget",
|
||||
title = R.string.setup_checklist_task_search_widget,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = false,
|
||||
),
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.EXPLORE_EXTENSION,
|
||||
title = "Explore extensions",
|
||||
title = R.string.setup_checklist_task_explore_extensions,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
isCompleted = false,
|
||||
),
|
||||
@@ -247,11 +239,13 @@ private fun SetupChecklistPreview(
|
||||
) {
|
||||
SetupChecklist(
|
||||
setupChecklistState = initialState,
|
||||
interactor = object : SetupChecklistInteractor {
|
||||
override fun onChecklistItemClicked(item: ChecklistItem) { /* no op */ }
|
||||
override fun onRemoveChecklistButtonClicked() { /* no op */ }
|
||||
},
|
||||
title = "Finish setting up Firefox",
|
||||
subtitle = "Complete all 6 steps to set up Firefox for the best browsing experience.",
|
||||
labelRemoveChecklistButton = "Remove checklist",
|
||||
onChecklistItemClicked = {},
|
||||
onRemoveChecklistButtonClicked = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ import org.mozilla.fenix.autofill.AutofillSearchActivity
|
||||
import org.mozilla.fenix.autofill.AutofillUnlockActivity
|
||||
import org.mozilla.fenix.components.appstate.AppAction
|
||||
import org.mozilla.fenix.components.appstate.AppState
|
||||
import org.mozilla.fenix.components.appstate.setup.checklist.SetupChecklistState
|
||||
import org.mozilla.fenix.components.appstate.setup.checklist.getSetupChecklistCollection
|
||||
import org.mozilla.fenix.components.metrics.MetricsMiddleware
|
||||
import org.mozilla.fenix.crashes.CrashReportingAppMiddleware
|
||||
import org.mozilla.fenix.crashes.SettingsCrashReportCache
|
||||
@@ -49,6 +51,7 @@ import org.mozilla.fenix.home.blocklist.BlocklistHandler
|
||||
import org.mozilla.fenix.home.blocklist.BlocklistMiddleware
|
||||
import org.mozilla.fenix.home.middleware.HomeTelemetryMiddleware
|
||||
import org.mozilla.fenix.messaging.state.MessagingMiddleware
|
||||
import org.mozilla.fenix.nimbus.FxNimbus
|
||||
import org.mozilla.fenix.onboarding.FenixOnboarding
|
||||
import org.mozilla.fenix.perf.AppStartReasonProvider
|
||||
import org.mozilla.fenix.perf.StartupActivityLog
|
||||
@@ -224,6 +227,7 @@ class Components(private val context: Context) {
|
||||
emptyList()
|
||||
},
|
||||
recentHistory = emptyList(),
|
||||
setupChecklistState = setupChecklistState(),
|
||||
).run { filterState(blocklistHandler) },
|
||||
middlewares = listOf(
|
||||
BlocklistMiddleware(blocklistHandler),
|
||||
@@ -250,6 +254,13 @@ class Components(private val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupChecklistState() = if (settings.showSetupChecklist) {
|
||||
val type = FxNimbus.features.setupChecklist.value().setupChecklistType
|
||||
SetupChecklistState(checklistItems = getSetupChecklistCollection(type))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val remoteSettingsService = lazyMonitored {
|
||||
RemoteSettingsService(
|
||||
context,
|
||||
|
||||
@@ -5,27 +5,29 @@
|
||||
package org.mozilla.fenix.components.appstate.setup.checklist
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.nimbus.SetupChecklistType
|
||||
|
||||
/**
|
||||
* A sealed class representing an item in the setup checklist.
|
||||
*
|
||||
* @property title the title of the checklist item.
|
||||
* @property title the string resource for the checklist item title.
|
||||
*/
|
||||
sealed class ChecklistItem(open val title: String) {
|
||||
|
||||
sealed class ChecklistItem(@StringRes open val title: Int) {
|
||||
/**
|
||||
* A data class representing an individual task in the checklist.
|
||||
*
|
||||
* @property type The type of the task.
|
||||
* @property title the task's title.
|
||||
* @property icon The icon resource to be displayed on the right of the task.
|
||||
* @property type the type of the task.
|
||||
* @property title the string resource of the task's title.
|
||||
* @property icon the icon resource to be displayed on the right of the task.
|
||||
* @property isCompleted whether the task has been completed.
|
||||
*/
|
||||
data class Task(
|
||||
val type: Type,
|
||||
override val title: String,
|
||||
@StringRes override val title: Int,
|
||||
@DrawableRes val icon: Int,
|
||||
val isCompleted: Boolean,
|
||||
val isCompleted: Boolean = false,
|
||||
) : ChecklistItem(title) {
|
||||
|
||||
/**
|
||||
@@ -44,14 +46,14 @@ sealed class ChecklistItem(open val title: String) {
|
||||
/**
|
||||
* A data class representing a group of tasks in the checklist.
|
||||
*
|
||||
* @property title the group's title.
|
||||
* @property title the string resource of the group's title.
|
||||
* @property tasks the list of tasks that belong to this group.
|
||||
* @property isExpanded whether the group is currently expanded.
|
||||
*/
|
||||
data class Group(
|
||||
override val title: String,
|
||||
@StringRes override val title: Int,
|
||||
val tasks: List<Task>,
|
||||
val isExpanded: Boolean,
|
||||
val isExpanded: Boolean = false,
|
||||
) : ChecklistItem(title) {
|
||||
val progress: Progress = tasks.getTaskProgress()
|
||||
}
|
||||
@@ -60,14 +62,13 @@ sealed class ChecklistItem(open val title: String) {
|
||||
/**
|
||||
* A data class representing the task progress.
|
||||
*
|
||||
* @property totalTasks The total number of tasks in the checklist.
|
||||
* @property completedTasks The number of completed tasks.
|
||||
* @property totalTasks the total number of tasks in the checklist.
|
||||
* @property completedTasks the number of completed tasks.
|
||||
*/
|
||||
data class Progress(
|
||||
var totalTasks: Int = 0,
|
||||
var completedTasks: Int = 0,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Checks if all tasks in the checklist are completed.
|
||||
*/
|
||||
@@ -90,3 +91,68 @@ fun List<ChecklistItem>.getTaskProgress(): Progress {
|
||||
completedTasks = tasks.count { it.isCompleted },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the checklist items for the given [SetupChecklistType].
|
||||
*/
|
||||
fun getSetupChecklistCollection(collection: SetupChecklistType) = when (collection) {
|
||||
SetupChecklistType.COLLECTION_1 -> createTasksCollection()
|
||||
SetupChecklistType.COLLECTION_2 -> createGroupsCollection()
|
||||
}
|
||||
|
||||
private fun createTasksCollection() =
|
||||
listOf(defaultBrowserTask(), exploreExtensionTask(), signInTask())
|
||||
|
||||
private fun defaultBrowserTask() = ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SET_AS_DEFAULT,
|
||||
title = R.string.setup_checklist_task_default_browser,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
)
|
||||
|
||||
private fun exploreExtensionTask() = ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.EXPLORE_EXTENSION,
|
||||
title = R.string.setup_checklist_task_explore_extensions,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
)
|
||||
|
||||
private fun signInTask() = ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SIGN_IN,
|
||||
title = R.string.setup_checklist_task_account_sync,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
)
|
||||
|
||||
private fun createGroupsCollection() =
|
||||
listOf(essentialsGroup(), customizeGroup(), helpfulToolsGroup())
|
||||
|
||||
private fun essentialsGroup() = ChecklistItem.Group(
|
||||
title = R.string.setup_checklist_group_essentials,
|
||||
tasks = listOf(defaultBrowserTask(), signInTask()),
|
||||
)
|
||||
|
||||
private fun customizeGroup() = ChecklistItem.Group(
|
||||
title = R.string.setup_checklist_group_customize,
|
||||
tasks = listOf(
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.SELECT_THEME,
|
||||
title = R.string.setup_checklist_task_theme_selection,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
),
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.CHANGE_TOOLBAR_PLACEMENT,
|
||||
title = R.string.setup_checklist_task_theme_selection,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
private fun helpfulToolsGroup() = ChecklistItem.Group(
|
||||
title = R.string.setup_checklist_group_helpful_tools,
|
||||
tasks = listOf(
|
||||
ChecklistItem.Task(
|
||||
type = ChecklistItem.Task.Type.INSTALL_SEARCH_WIDGET,
|
||||
title = R.string.setup_checklist_task_theme_selection,
|
||||
icon = R.drawable.mozac_ic_web_extension_default_icon,
|
||||
),
|
||||
exploreExtensionTask(),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -27,6 +27,7 @@ import mozilla.components.service.pocket.PocketStory.PocketSponsoredStoryShim
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.components.appstate.AppState
|
||||
import org.mozilla.fenix.components.appstate.setup.checklist.ChecklistItem
|
||||
import org.mozilla.fenix.compose.MessageCardColors
|
||||
import org.mozilla.fenix.compose.MessageCardState
|
||||
import org.mozilla.fenix.compose.SelectableChipColors
|
||||
@@ -109,6 +110,10 @@ internal object FakeHomepagePreview {
|
||||
override fun showWallpapersOnboardingDialog(state: WallpaperState): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onChecklistItemClicked(item: ChecklistItem) { /* no op */ }
|
||||
|
||||
override fun onRemoveChecklistButtonClicked() { /* no op */ }
|
||||
}
|
||||
|
||||
internal val privateBrowsingInteractor
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.mozilla.fenix.home.recentvisits.interactor.RecentVisitsInteractor
|
||||
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
|
||||
import org.mozilla.fenix.home.sessioncontrol.CustomizeHomeIteractor
|
||||
import org.mozilla.fenix.home.sessioncontrol.MessageCardInteractor
|
||||
import org.mozilla.fenix.home.sessioncontrol.SetupChecklistInteractor
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabSessionInteractor
|
||||
import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor
|
||||
import org.mozilla.fenix.home.sessioncontrol.WallpaperInteractor
|
||||
@@ -36,4 +37,5 @@ interface HomepageInteractor :
|
||||
PocketStoriesInteractor,
|
||||
PrivateBrowsingInteractor,
|
||||
SearchSelectorInteractor,
|
||||
WallpaperInteractor
|
||||
WallpaperInteractor,
|
||||
SetupChecklistInteractor
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.CustomizeHomeButtonView
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoCollectionsMessageViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.PrivateBrowsingDescriptionViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.MessageCardViewHolder
|
||||
import org.mozilla.fenix.home.setup.ui.SetupChecklistViewHolder
|
||||
import org.mozilla.fenix.home.topsites.TopSitePagerViewHolder
|
||||
import org.mozilla.fenix.home.topsites.TopSitesViewHolder
|
||||
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||
@@ -169,6 +170,11 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
|
||||
object PocketCategoriesItem : AdapterItem(PocketCategoriesViewHolder.LAYOUT_ID)
|
||||
object PocketRecommendationsFooterItem : AdapterItem(PocketRecommendationsHeaderViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
* Adapter item to hold the setup checklist feature view.
|
||||
*/
|
||||
data object SetupChecklist : AdapterItem(SetupChecklistViewHolder.LAYOUT_ID)
|
||||
|
||||
object BottomSpacer : AdapterItem(BottomSpacerViewHolder.LAYOUT_ID)
|
||||
|
||||
/**
|
||||
@@ -291,6 +297,11 @@ class SessionControlAdapter(
|
||||
viewLifecycleOwner = viewLifecycleOwner,
|
||||
interactor = interactor,
|
||||
)
|
||||
SetupChecklistViewHolder.LAYOUT_ID -> return SetupChecklistViewHolder(
|
||||
composeView = ComposeView(parent.context),
|
||||
viewLifecycleOwner = viewLifecycleOwner,
|
||||
interactor = interactor,
|
||||
)
|
||||
}
|
||||
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
@@ -331,6 +342,7 @@ class SessionControlAdapter(
|
||||
is PocketRecommendationsHeaderViewHolder,
|
||||
is PocketStoriesViewHolder,
|
||||
is TopSitesViewHolder,
|
||||
is SetupChecklistViewHolder,
|
||||
-> {
|
||||
// no op
|
||||
// This previously called "composeView.disposeComposition" which would have the
|
||||
|
||||
@@ -54,6 +54,7 @@ import org.mozilla.fenix.components.AppStore
|
||||
import org.mozilla.fenix.components.TabCollectionStorage
|
||||
import org.mozilla.fenix.components.appstate.AppAction
|
||||
import org.mozilla.fenix.components.appstate.AppState
|
||||
import org.mozilla.fenix.components.appstate.setup.checklist.ChecklistItem
|
||||
import org.mozilla.fenix.components.metrics.MetricsUtils
|
||||
import org.mozilla.fenix.components.toolbar.navbar.shouldAddNavigationBar
|
||||
import org.mozilla.fenix.ext.components
|
||||
@@ -189,6 +190,16 @@ interface SessionControlController {
|
||||
* @see [SessionControlInteractor.reportSessionMetrics]
|
||||
*/
|
||||
fun handleReportSessionMetrics(state: AppState)
|
||||
|
||||
/**
|
||||
* @see [SetupChecklistInteractor.onChecklistItemClicked]
|
||||
*/
|
||||
fun onChecklistItemClicked(item: ChecklistItem)
|
||||
|
||||
/**
|
||||
* @see [SetupChecklistInteractor.onRemoveChecklistButtonClicked]
|
||||
*/
|
||||
fun onRemoveChecklistButtonClicked()
|
||||
}
|
||||
|
||||
@Suppress("TooManyFunctions", "LargeClass", "LongParameterList")
|
||||
@@ -663,4 +674,12 @@ class DefaultSessionControlController(
|
||||
|
||||
HomeBookmarks.bookmarksCount.set(state.bookmarks.size.toLong())
|
||||
}
|
||||
|
||||
override fun onChecklistItemClicked(item: ChecklistItem) {
|
||||
appStore.dispatch(AppAction.SetupChecklistAction.ChecklistItemClicked(item))
|
||||
}
|
||||
|
||||
override fun onRemoveChecklistButtonClicked() {
|
||||
appStore.dispatch(AppAction.SetupChecklistAction.Closed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import mozilla.components.service.nimbus.messaging.Message
|
||||
import mozilla.components.service.pocket.PocketStory
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.components.appstate.AppState
|
||||
import org.mozilla.fenix.components.appstate.setup.checklist.ChecklistItem
|
||||
import org.mozilla.fenix.home.bookmarks.Bookmark
|
||||
import org.mozilla.fenix.home.bookmarks.controller.BookmarksController
|
||||
import org.mozilla.fenix.home.interactor.HomepageInteractor
|
||||
@@ -218,6 +219,21 @@ interface WallpaperInteractor {
|
||||
fun showWallpapersOnboardingDialog(state: WallpaperState): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for setup checklist feature related actions.
|
||||
*/
|
||||
interface SetupChecklistInteractor {
|
||||
/**
|
||||
* Gets invoked when the user clicks a check list item.
|
||||
*/
|
||||
fun onChecklistItemClicked(item: ChecklistItem)
|
||||
|
||||
/**
|
||||
* Invoked when the remove button is clicked.
|
||||
*/
|
||||
fun onRemoveChecklistButtonClicked()
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactor for the Home screen. Provides implementations for the CollectionInteractor,
|
||||
* OnboardingInteractor, TopSiteInteractor, TabSessionInteractor, ToolbarInteractor,
|
||||
@@ -301,6 +317,14 @@ class SessionControlInteractor(
|
||||
return controller.handleShowWallpapersOnboardingDialog(state)
|
||||
}
|
||||
|
||||
override fun onChecklistItemClicked(item: ChecklistItem) {
|
||||
controller.onChecklistItemClicked(item)
|
||||
}
|
||||
|
||||
override fun onRemoveChecklistButtonClicked() {
|
||||
controller.onRemoveChecklistButtonClicked()
|
||||
}
|
||||
|
||||
override fun onToggleCollectionExpanded(collection: TabCollection, expand: Boolean) {
|
||||
controller.handleToggleCollectionExpanded(collection, expand)
|
||||
}
|
||||
|
||||
@@ -67,6 +67,10 @@ internal fun normalModeAdapterItems(
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.showSetupChecklist) {
|
||||
items.add(AdapterItem.SetupChecklist)
|
||||
}
|
||||
|
||||
if (showRecentTab) {
|
||||
shouldShowCustomizeHome = true
|
||||
items.add(AdapterItem.RecentTabsHeader)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/* 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.setup.ui
|
||||
|
||||
import android.view.View
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import mozilla.components.lib.state.ext.observeAsComposableState
|
||||
import org.mozilla.fenix.checklist.SetupChecklist
|
||||
import org.mozilla.fenix.components.components
|
||||
import org.mozilla.fenix.compose.ComposeViewHolder
|
||||
import org.mozilla.fenix.home.sessioncontrol.SetupChecklistInteractor
|
||||
|
||||
/**
|
||||
* View holder for the Setup Checklist feature.
|
||||
*
|
||||
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
|
||||
* @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
|
||||
* @param interactor [SetupChecklistInteractor] to handle user interactions.
|
||||
*/
|
||||
class SetupChecklistViewHolder(
|
||||
composeView: ComposeView,
|
||||
viewLifecycleOwner: LifecycleOwner,
|
||||
private val interactor: SetupChecklistInteractor,
|
||||
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val appStore = components.appStore
|
||||
val setupChecklistState =
|
||||
appStore.observeAsComposableState { state -> state.setupChecklistState }.value
|
||||
|
||||
if (setupChecklistState != null) {
|
||||
SetupChecklist(
|
||||
setupChecklistState = setupChecklistState,
|
||||
interactor = interactor,
|
||||
title = "Finish setting up Firefox",
|
||||
subtitle = "Complete all 6 steps to set up Firefox for the best browsing experience.",
|
||||
labelRemoveChecklistButton = "Remove checklist",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The layout ID for this view holder.
|
||||
*/
|
||||
companion object {
|
||||
val LAYOUT_ID = View.generateViewId()
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import mozilla.components.feature.top.sites.TopSite
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
||||
import org.mozilla.fenix.components.appstate.AppState
|
||||
import org.mozilla.fenix.components.appstate.setup.checklist.SetupChecklistState
|
||||
import org.mozilla.fenix.components.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.ext.shouldShowRecentSyncedTabs
|
||||
@@ -73,6 +74,7 @@ internal sealed class HomepageState {
|
||||
* @property showRecentlyVisited Whether to show recent history section.
|
||||
* @property showPocketStories Whether to show the pocket stories section.
|
||||
* @property showSearchBar Whether to show the search bar.
|
||||
* @property setupChecklistState Optional state of the setup checklist feature.
|
||||
* @property topSiteColors The color set defined by [TopSiteColors] used to style a top site.
|
||||
* @property cardBackgroundColor Background color for card items.
|
||||
* @property buttonBackgroundColor Background [Color] for buttons.
|
||||
@@ -97,6 +99,7 @@ internal sealed class HomepageState {
|
||||
val showRecentlyVisited: Boolean,
|
||||
val showPocketStories: Boolean,
|
||||
val showSearchBar: Boolean,
|
||||
val setupChecklistState: SetupChecklistState?,
|
||||
val topSiteColors: TopSiteColors,
|
||||
val cardBackgroundColor: Color,
|
||||
val buttonBackgroundColor: Color,
|
||||
@@ -167,6 +170,7 @@ internal sealed class HomepageState {
|
||||
showPocketStories = settings.showPocketRecommendationsFeature &&
|
||||
recommendationState.pocketStories.isNotEmpty() && firstFrameDrawn,
|
||||
showSearchBar = settings.enableHomepageSearchBar,
|
||||
setupChecklistState = setupChecklistState,
|
||||
topSiteColors = TopSiteColors.colors(wallpaperState = wallpaperState),
|
||||
cardBackgroundColor = wallpaperState.cardBackgroundColor,
|
||||
buttonBackgroundColor = wallpaperState.buttonBackgroundColor,
|
||||
|
||||
@@ -27,6 +27,7 @@ import mozilla.telemetry.glean.private.NoExtras
|
||||
import org.mozilla.fenix.GleanMetrics.History
|
||||
import org.mozilla.fenix.GleanMetrics.RecentlyVisitedHomepage
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.checklist.SetupChecklist
|
||||
import org.mozilla.fenix.compose.MessageCard
|
||||
import org.mozilla.fenix.compose.button.TertiaryButton
|
||||
import org.mozilla.fenix.compose.home.HomeSectionHeader
|
||||
@@ -125,6 +126,16 @@ internal fun Homepage(
|
||||
)
|
||||
}
|
||||
|
||||
if (setupChecklistState != null) {
|
||||
SetupChecklist(
|
||||
setupChecklistState = setupChecklistState,
|
||||
interactor = interactor,
|
||||
title = "Finish setting up Firefox",
|
||||
subtitle = "Complete all 6 steps to set up Firefox for the best browsing experience.",
|
||||
labelRemoveChecklistButton = "Remove checklist",
|
||||
)
|
||||
}
|
||||
|
||||
if (showRecentTabs) {
|
||||
RecentTabsSection(
|
||||
interactor = interactor,
|
||||
@@ -413,6 +424,7 @@ private fun HomepagePreview() {
|
||||
showRecentlyVisited = true,
|
||||
showPocketStories = true,
|
||||
showSearchBar = true,
|
||||
setupChecklistState = null,
|
||||
topSiteColors = TopSiteColors.colors(),
|
||||
cardBackgroundColor = WallpaperState.default.cardBackgroundColor,
|
||||
buttonTextColor = WallpaperState.default.buttonTextColor,
|
||||
@@ -448,6 +460,7 @@ private fun HomepagePreviewCollections() {
|
||||
showRecentlyVisited = true,
|
||||
showPocketStories = true,
|
||||
showSearchBar = true,
|
||||
setupChecklistState = null,
|
||||
topSiteColors = TopSiteColors.colors(),
|
||||
cardBackgroundColor = WallpaperState.default.cardBackgroundColor,
|
||||
buttonTextColor = WallpaperState.default.buttonTextColor,
|
||||
|
||||
@@ -2496,4 +2496,10 @@ class Settings(private val appContext: Context) : PreferencesHolder {
|
||||
key = appContext.getPreferenceKey(R.string.pref_key_setup_step_extensions),
|
||||
default = false,
|
||||
)
|
||||
|
||||
/**
|
||||
* Indicates whether or not to show the checklist feature.
|
||||
*/
|
||||
val showSetupChecklist: Boolean
|
||||
get() = FxNimbus.features.setupChecklist.value().enabled
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user