For #22383: Propagate ViewTreeLifecycleOwner to composable view holders.

upstream-sync
mcarare 3 years ago committed by mergify[bot]
parent 047ec890b2
commit 58a6fafabc

@ -0,0 +1,50 @@
/* 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.compose
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewTreeLifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.ViewTreeSavedStateRegistryOwner
import org.mozilla.fenix.theme.FirefoxTheme
/**
* [RecyclerView.ViewHolder] used for Jetpack Compose UI content .
*
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
* @param viewLifecycleOwner [LifecycleOwner] life cycle owner for the view.
*/
abstract class ComposeViewHolder(
val composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner
) : RecyclerView.ViewHolder(composeView) {
/**
* Composable that contains the content for a specific [ComposeViewHolder] implementation.
*/
@Composable
abstract fun Content()
init {
composeView.setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
composeView.setContent {
FirefoxTheme {
Content()
}
}
ViewTreeLifecycleOwner.set(composeView, viewLifecycleOwner)
ViewTreeSavedStateRegistryOwner.set(
composeView,
viewLifecycleOwner as SavedStateRegistryOwner
)
}
}

@ -17,16 +17,16 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.LifecycleOwner
import androidx.recyclerview.widget.RecyclerView
import mozilla.components.lib.state.ext.observeAsComposableState
import mozilla.components.service.pocket.PocketRecommendedStory
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.compose.SectionHeader
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.theme.FirefoxTheme
internal const val POCKET_STORIES_TO_SHOW_COUNT = 8
internal const val POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT = 8
@ -40,35 +40,30 @@ internal const val POCKET_CATEGORIES_SELECTED_AT_A_TIME_COUNT = 8
* @param interactor [PocketStoriesInteractor] callback for user interaction.
*/
class PocketStoriesViewHolder(
val composeView: ComposeView,
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
val store: HomeFragmentStore,
val interactor: PocketStoriesInteractor
) : RecyclerView.ViewHolder(composeView) {
init {
composeView.setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
composeView.setContent {
FirefoxTheme {
PocketStories(
store,
interactor::onStoriesShown,
interactor::onStoryClicked,
interactor::onCategoryClicked,
interactor::onDiscoverMoreClicked,
interactor::onLearnMoreClicked,
with(composeView.resources) {
getDimensionPixelSize(R.dimen.home_item_horizontal_margin) / displayMetrics.density
}
)
}
}
}
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
companion object {
val LAYOUT_ID = View.generateViewId()
}
@Composable
override fun Content() {
PocketStories(
store,
interactor::onStoriesShown,
interactor::onStoryClicked,
interactor::onCategoryClicked,
interactor::onDiscoverMoreClicked,
interactor::onLearnMoreClicked,
with(composeView.resources) {
getDimensionPixelSize(R.dimen.home_item_horizontal_margin) / displayMetrics.density
}
)
}
}
@Composable
@ -110,7 +105,12 @@ fun PocketStories(
Spacer(Modifier.height(17.dp))
PocketStories(stories ?: emptyList(), horizontalPadding.dp, onStoryClicked, onDiscoverMoreClicked)
PocketStories(
stories ?: emptyList(),
horizontalPadding.dp,
onStoryClicked,
onDiscoverMoreClicked
)
Spacer(Modifier.height(24.dp))

@ -5,42 +5,39 @@
package org.mozilla.fenix.home.recentbookmarks.view
import android.view.View
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.LifecycleOwner
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recentbookmarks.interactor.RecentBookmarksInteractor
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.utils.view.ViewHolder
class RecentBookmarksViewHolder(
val composeView: ComposeView,
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val store: HomeFragmentStore,
val interactor: RecentBookmarksInteractor,
val metrics: MetricController
) : ViewHolder(composeView) {
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
init {
metrics.track(Event.RecentBookmarksShown)
composeView.setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
composeView.setContent {
val recentBookmarks = store.observeAsComposableState { state -> state.recentBookmarks }
FirefoxTheme {
RecentBookmarks(
bookmarks = recentBookmarks.value ?: emptyList(),
onRecentBookmarkClick = interactor::onRecentBookmarkClicked
)
}
}
}
companion object {
val LAYOUT_ID = View.generateViewId()
}
@Composable
override fun Content() {
val recentBookmarks = store.observeAsComposableState { state -> state.recentBookmarks }
RecentBookmarks(
bookmarks = recentBookmarks.value ?: emptyList(),
onRecentBookmarkClick = interactor::onRecentBookmarkClicked
)
}
}

@ -5,14 +5,14 @@
package org.mozilla.fenix.home.recenttabs.view
import android.view.View
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.LifecycleOwner
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recenttabs.interactor.RecentTabInteractor
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.utils.view.ViewHolder
/**
* View holder for a recent tab item.
@ -22,32 +22,30 @@ import org.mozilla.fenix.utils.view.ViewHolder
* @param interactor [RecentTabInteractor] which will have delegated to all user interactions.
*/
class RecentTabViewHolder(
val composeView: ComposeView,
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val store: HomeFragmentStore,
private val interactor: RecentTabInteractor
) : ViewHolder(composeView) {
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
init {
val horizontalPadding = composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
val horizontalPadding =
composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0)
composeView.setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
composeView.setContent {
val recentTabs = store.observeAsComposableState { state -> state.recentTabs }
FirefoxTheme {
RecentTabs(
recentTabs = recentTabs.value ?: emptyList(),
onRecentTabClick = { interactor.onRecentTabClicked(it) },
onRecentSearchGroupClicked = { interactor.onRecentSearchGroupClicked(it) }
)
}
}
}
companion object {
val LAYOUT_ID = View.generateViewId()
}
@Composable
override fun Content() {
val recentTabs = store.observeAsComposableState { state -> state.recentTabs }
RecentTabs(
recentTabs = recentTabs.value ?: emptyList(),
onRecentTabClick = { interactor.onRecentTabClicked(it) },
onRecentSearchGroupClicked = { interactor.onRecentSearchGroupClicked(it) }
)
}
}

@ -5,20 +5,20 @@
package org.mozilla.fenix.home.recentvisits.view
import android.view.View
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.LifecycleOwner
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryGroup
import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryHighlight
import org.mozilla.fenix.home.recentvisits.interactor.RecentVisitsInteractor
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.utils.view.ViewHolder
/**
* View holder for [RecentlyVisitedItem]s.
@ -29,50 +29,50 @@ import org.mozilla.fenix.utils.view.ViewHolder
* @property metrics [MetricController] that handles telemetry events.
*/
class RecentlyVisitedViewHolder(
val composeView: ComposeView,
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val store: HomeFragmentStore,
private val interactor: RecentVisitsInteractor,
private val metrics: MetricController
) : ViewHolder(composeView) {
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
init {
val horizontalPadding = composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
val horizontalPadding =
composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0)
}
composeView.setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
composeView.setContent {
val recentVisits = store.observeAsComposableState { state -> state.recentHistory }
@Composable
override fun Content() {
val recentVisits = store.observeAsComposableState { state -> state.recentHistory }
FirefoxTheme {
RecentlyVisited(
recentVisits = recentVisits.value ?: emptyList(),
menuItems = listOfNotNull(
RecentVisitMenuItem(
title = stringResource(R.string.recently_visited_menu_item_remove),
onClick = { visit ->
when (visit) {
is RecentHistoryGroup -> interactor.onRemoveRecentHistoryGroup(visit.title)
is RecentHistoryHighlight -> interactor.onRemoveRecentHistoryHighlight(visit.url)
}
}
)
),
onRecentVisitClick = { recentlyVisitedItem, pageNumber ->
when (recentlyVisitedItem) {
is RecentHistoryHighlight -> {
interactor.onRecentHistoryHighlightClicked(recentlyVisitedItem)
}
is RecentHistoryGroup -> {
metrics.track(Event.HistoryRecentSearchesTapped(pageNumber.toString()))
interactor.onRecentHistoryGroupClicked(recentlyVisitedItem)
}
RecentlyVisited(
recentVisits = recentVisits.value ?: emptyList(),
menuItems = listOfNotNull(
RecentVisitMenuItem(
title = stringResource(R.string.recently_visited_menu_item_remove),
onClick = { visit ->
when (visit) {
is RecentHistoryGroup -> interactor.onRemoveRecentHistoryGroup(visit.title)
is RecentHistoryHighlight -> interactor.onRemoveRecentHistoryHighlight(
visit.url
)
}
}
)
),
onRecentVisitClick = { recentlyVisitedItem, pageNumber ->
when (recentlyVisitedItem) {
is RecentHistoryHighlight -> {
interactor.onRecentHistoryHighlightClicked(recentlyVisitedItem)
}
is RecentHistoryGroup -> {
metrics.track(Event.HistoryRecentSearchesTapped(pageNumber.toString()))
interactor.onRecentHistoryGroupClicked(recentlyVisitedItem)
}
}
}
}
)
}
companion object {

@ -19,6 +19,7 @@ import mozilla.components.feature.top.sites.TopSite.Type.FRECENT
import mozilla.components.ui.widgets.WidgetSiteItemView
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.tips.Tip
import org.mozilla.fenix.home.BottomSpacerViewHolder
import org.mozilla.fenix.home.HomeFragmentStore
import org.mozilla.fenix.home.TopPlaceholderViewHolder
import org.mozilla.fenix.home.pocket.PocketStoriesViewHolder
@ -44,7 +45,6 @@ import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTh
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingToolbarPositionPickerViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding.OnboardingTrackingProtectionViewHolder
import org.mozilla.fenix.home.tips.ButtonTipViewHolder
import org.mozilla.fenix.home.BottomSpacerViewHolder
import org.mozilla.fenix.home.topsites.TopSitePagerViewHolder
import mozilla.components.feature.tab.collections.Tab as ComponentTab
@ -211,26 +211,31 @@ class SessionControlAdapter(
when (viewType) {
CustomizeHomeButtonViewHolder.LAYOUT_ID -> return CustomizeHomeButtonViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner,
interactor = interactor
)
PocketStoriesViewHolder.LAYOUT_ID -> return PocketStoriesViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner,
store = store,
interactor = interactor
)
RecentBookmarksViewHolder.LAYOUT_ID -> return RecentBookmarksViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner,
store = store,
interactor = interactor,
metrics = components.analytics.metrics
)
RecentTabViewHolder.LAYOUT_ID -> return RecentTabViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner,
store = store,
interactor = interactor
)
RecentlyVisitedViewHolder.LAYOUT_ID -> return RecentlyVisitedViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner,
store = store,
interactor = interactor,
metrics = components.analytics.metrics

@ -18,44 +18,39 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.recyclerview.widget.RecyclerView
import androidx.lifecycle.LifecycleOwner
import mozilla.components.ui.colors.PhotonColors
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.home.sessioncontrol.CustomizeHomeIteractor
import org.mozilla.fenix.theme.FirefoxTheme
class CustomizeHomeButtonViewHolder(
val composeView: ComposeView,
composeView: ComposeView,
viewLifecycleOwner: LifecycleOwner,
private val interactor: CustomizeHomeIteractor
) : RecyclerView.ViewHolder(composeView) {
init {
composeView.setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
composeView.setContent {
FirefoxTheme {
Column {
Spacer(modifier = Modifier.height(68.dp))
CustomizeHomeButton(
onButtonClick = { interactor.openCustomizeHomePage() }
)
}
}
}
}
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
companion object {
val LAYOUT_ID = View.generateViewId()
}
@Composable
override fun Content() {
Column {
Spacer(modifier = Modifier.height(68.dp))
CustomizeHomeButton(
onButtonClick = { interactor.openCustomizeHomePage() }
)
}
}
}
/**

Loading…
Cancel
Save