Bug 1844967 - Improve performance of tab thumbnail loading in Compose

fenix/118.0
Noah Bond 11 months ago committed by mergify[bot]
parent a314d38140
commit fa9c47d5fa

@ -16,14 +16,15 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.concept.base.images.ImageLoadRequest
import org.mozilla.fenix.theme.FirefoxTheme
private const val THUMBNAIL_SIZE = 108
private const val FALLBACK_ICON_SIZE = 36
/**
@ -31,9 +32,10 @@ private const val FALLBACK_ICON_SIZE = 36
* will be displayed until the thumbnail is loaded.
*
* @param tab The given [TabSessionState] to render a thumbnail for.
* @param size [Dp] size of the thumbnail.
* @param backgroundColor [Color] used for the background of the favicon.
* @param storage [ThumbnailStorage] to obtain tab thumbnail bitmaps from.
* @param size Size of the thumbnail.
* @param modifier [Modifier] used to draw the image content.
* @param backgroundColor [Color] used for the background of the favicon.
* @param contentDescription Text used by accessibility services
* to describe what this image represents.
* @param contentScale [ContentScale] used to draw image content.
@ -43,8 +45,9 @@ private const val FALLBACK_ICON_SIZE = 36
@Suppress("LongParameterList")
fun TabThumbnail(
tab: TabSessionState,
storage: ThumbnailStorage,
size: Int,
modifier: Modifier = Modifier,
size: Dp = THUMBNAIL_SIZE.dp,
backgroundColor: Color = FirefoxTheme.colors.layer2,
contentDescription: String? = null,
contentScale: ContentScale = ContentScale.FillWidth,
@ -55,8 +58,11 @@ fun TabThumbnail(
backgroundColor = backgroundColor,
) {
ThumbnailImage(
key = tab.id,
size = size,
request = ImageLoadRequest(
id = tab.id,
size = size,
),
storage = storage,
modifier = modifier,
contentScale = contentScale,
alignment = alignment,
@ -93,8 +99,10 @@ private fun ThumbnailCardPreview() {
FirefoxTheme {
TabThumbnail(
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
size = 108,
storage = ThumbnailStorage(LocalContext.current),
modifier = Modifier
.size(THUMBNAIL_SIZE.dp, 80.dp)
.size(108.dp, 80.dp)
.clip(RoundedCornerShape(8.dp)),
)
}

@ -16,12 +16,14 @@ 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.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import mozilla.components.browser.icons.compose.Loader
import mozilla.components.browser.icons.compose.Placeholder
import mozilla.components.browser.icons.compose.WithIcon
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.concept.base.images.ImageLoadRequest
import org.mozilla.fenix.components.components
import org.mozilla.fenix.theme.FirefoxTheme
@ -33,10 +35,10 @@ private const val FALLBACK_ICON_SIZE = 36
* will be displayed until the thumbnail is loaded.
*
* @param url Url to display thumbnail for.
* @param key Key used to remember the thumbnail for future compositions.
* @param size [Dp] size of the thumbnail.
* @param backgroundColor [Color] used for the background of the favicon.
* @param request [ImageLoadRequest] used to fetch the thumbnail bitmap.
* @param storage [ThumbnailStorage] to obtain tab thumbnail bitmaps from.
* @param modifier [Modifier] used to draw the image content.
* @param backgroundColor [Color] used for the background of the favicon.
* @param contentDescription Text used by accessibility services
* to describe what this image represents.
* @param contentScale [ContentScale] used to draw image content.
@ -45,10 +47,10 @@ private const val FALLBACK_ICON_SIZE = 36
@Composable
fun ThumbnailCard(
url: String,
key: String,
size: Dp = THUMBNAIL_SIZE.dp,
backgroundColor: Color = FirefoxTheme.colors.layer2,
request: ImageLoadRequest,
storage: ThumbnailStorage,
modifier: Modifier = Modifier,
backgroundColor: Color = FirefoxTheme.colors.layer2,
contentDescription: String? = null,
contentScale: ContentScale = ContentScale.FillWidth,
alignment: Alignment = Alignment.TopCenter,
@ -58,8 +60,8 @@ fun ThumbnailCard(
backgroundColor = backgroundColor,
) {
ThumbnailImage(
key = key,
size = size,
request = request,
storage = storage,
modifier = modifier,
contentScale = contentScale,
alignment = alignment,
@ -95,7 +97,8 @@ private fun ThumbnailCardPreview() {
FirefoxTheme {
ThumbnailCard(
url = "https://mozilla.com",
key = "123",
request = ImageLoadRequest("123", THUMBNAIL_SIZE),
storage = ThumbnailStorage(LocalContext.current),
modifier = Modifier
.size(THUMBNAIL_SIZE.dp)
.clip(RoundedCornerShape(8.dp)),

@ -20,29 +20,28 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.concept.base.images.ImageLoadRequest
import org.mozilla.fenix.components.components
import org.mozilla.fenix.theme.FirefoxTheme
/**
* Thumbnail belonging to a [key]. Asynchronously fetches the bitmap from storage.
* Thumbnail belonging to a [ImageLoadRequest]. Asynchronously fetches the bitmap from storage.
*
* @param key Key used to remember the thumbnail for future compositions.
* @param size [Dp] size of the thumbnail.
* @param request [ImageLoadRequest] used to fetch the thumbnail bitmap.
* @param storage [ThumbnailStorage] to obtain tab thumbnail bitmaps from.
* @param modifier [Modifier] used to draw the image content.
* @param contentScale [ContentScale] used to draw image content.
* @param alignment [Alignment] used to draw the image content.
* @param fallbackContent The content to display with a thumbnail is unable to be loaded.
*/
@Composable
@Suppress("LongParameterList")
fun ThumbnailImage(
key: String,
size: Dp,
request: ImageLoadRequest,
storage: ThumbnailStorage,
modifier: Modifier,
contentScale: ContentScale,
alignment: Alignment,
@ -51,9 +50,6 @@ fun ThumbnailImage(
if (inComposePreview) {
Box(modifier = Modifier.background(color = FirefoxTheme.colors.layer3))
} else {
val thumbnailSize = LocalDensity.current.run { size.toPx().toInt() }
val request = ImageLoadRequest(key, thumbnailSize)
val storage = components.core.thumbnailStorage
var state by remember { mutableStateOf(ThumbnailImageState(null, false)) }
val scope = rememberCoroutineScope()
@ -113,8 +109,8 @@ private data class ThumbnailImageState(
private fun ThumbnailImagePreview() {
FirefoxTheme {
ThumbnailImage(
key = "",
size = 1.dp,
request = ImageLoadRequest("1", 1),
storage = ThumbnailStorage(LocalContext.current),
modifier = Modifier,
contentScale = ContentScale.Crop,
alignment = Alignment.Center,

@ -41,7 +41,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
@ -57,6 +57,7 @@ import androidx.compose.ui.zIndex
import androidx.core.text.BidiFormatter
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.support.ktx.kotlin.MAX_URI_LENGTH
import mozilla.components.ui.colors.PhotonColors
import org.mozilla.fenix.R
@ -74,6 +75,8 @@ import org.mozilla.fenix.theme.FirefoxTheme
* long clicks, multiple selection, and media controls.
*
* @param tab The given tab to be render as view a grid item.
* @param storage [ThumbnailStorage] to obtain tab thumbnail bitmaps from.
* @param thumbnailSize Size of tab's thumbnail.
* @param isSelected Indicates if the item should be render as selected.
* @param multiSelectionEnabled Indicates if the item should be render with multi selection options,
* enabled.
@ -89,6 +92,8 @@ import org.mozilla.fenix.theme.FirefoxTheme
@Suppress("MagicNumber", "LongParameterList", "LongMethod")
fun TabGridItem(
tab: TabSessionState,
storage: ThumbnailStorage,
thumbnailSize: Int,
isSelected: Boolean = false,
multiSelectionEnabled: Boolean = false,
multiSelectionSelected: Boolean = false,
@ -226,6 +231,8 @@ fun TabGridItem(
Thumbnail(
tab = tab,
size = thumbnailSize,
storage = storage,
multiSelectionSelected = multiSelectionSelected,
)
}
@ -253,6 +260,8 @@ fun TabGridItem(
@Composable
private fun Thumbnail(
tab: TabSessionState,
size: Int,
storage: ThumbnailStorage,
multiSelectionSelected: Boolean,
) {
Box(
@ -265,7 +274,8 @@ private fun Thumbnail(
) {
TabThumbnail(
tab = tab,
size = LocalConfiguration.current.screenWidthDp.dp,
size = size,
storage = storage,
modifier = Modifier.fillMaxSize(),
)
@ -305,6 +315,8 @@ private fun TabGridItemPreview() {
url = "www.mozilla.com",
title = "Mozilla Domain",
),
thumbnailSize = 108,
storage = ThumbnailStorage(LocalContext.current),
onCloseClick = {},
onMediaClick = {},
onClick = {},
@ -319,6 +331,8 @@ private fun TabGridItemSelectedPreview() {
FirefoxTheme {
TabGridItem(
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
thumbnailSize = 108,
storage = ThumbnailStorage(LocalContext.current),
isSelected = true,
onCloseClick = {},
onMediaClick = {},
@ -334,6 +348,8 @@ private fun TabGridItemMultiSelectedPreview() {
FirefoxTheme {
TabGridItem(
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
thumbnailSize = 108,
storage = ThumbnailStorage(LocalContext.current),
multiSelectionEnabled = true,
multiSelectionSelected = true,
onCloseClick = {},

@ -31,6 +31,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
@ -42,6 +43,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.support.ktx.kotlin.MAX_URI_LENGTH
import mozilla.components.ui.colors.PhotonColors
import org.mozilla.fenix.R
@ -57,7 +59,9 @@ import org.mozilla.fenix.theme.FirefoxTheme
* List item used to display a tab that supports clicks,
* long clicks, multiselection, and media controls.
*
* @param tab The given tab to be render as view a list item.
* @param tab The given tab to be render as view a grid item.
* @param storage [ThumbnailStorage] to obtain tab thumbnail bitmaps from.
* @param thumbnailSize Size of tab's thumbnail.
* @param isSelected Indicates if the item should be render as selected.
* @param multiSelectionEnabled Indicates if the item should be render with multi selection options,
* enabled.
@ -70,9 +74,11 @@ import org.mozilla.fenix.theme.FirefoxTheme
*/
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterialApi::class)
@Composable
@Suppress("MagicNumber", "LongMethod")
@Suppress("MagicNumber", "LongMethod", "LongParameterList")
fun TabListItem(
tab: TabSessionState,
storage: ThumbnailStorage,
thumbnailSize: Int,
isSelected: Boolean = false,
multiSelectionEnabled: Boolean = false,
multiSelectionSelected: Boolean = false,
@ -130,6 +136,8 @@ fun TabListItem(
) {
Thumbnail(
tab = tab,
size = thumbnailSize,
storage = storage,
multiSelectionEnabled = multiSelectionEnabled,
isSelected = multiSelectionSelected,
onMediaIconClicked = { onMediaClick(it) },
@ -184,8 +192,11 @@ fun TabListItem(
}
@Composable
@Suppress("LongParameterList")
private fun Thumbnail(
tab: TabSessionState,
size: Int,
storage: ThumbnailStorage,
multiSelectionEnabled: Boolean,
isSelected: Boolean,
onMediaIconClicked: ((TabSessionState) -> Unit),
@ -194,6 +205,8 @@ private fun Thumbnail(
Box {
TabThumbnail(
tab = tab,
size = size,
storage = storage,
modifier = Modifier
.size(width = 92.dp, height = 72.dp)
.semantics(mergeDescendants = true) {
@ -245,6 +258,8 @@ private fun TabListItemPreview() {
FirefoxTheme {
TabListItem(
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
thumbnailSize = 108,
storage = ThumbnailStorage(LocalContext.current),
onCloseClick = {},
onMediaClick = {},
onClick = {},
@ -259,6 +274,8 @@ private fun SelectedTabListItemPreview() {
FirefoxTheme {
TabListItem(
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
thumbnailSize = 108,
storage = ThumbnailStorage(LocalContext.current),
onCloseClick = {},
onMediaClick = {},
onClick = {},

@ -33,12 +33,16 @@ 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.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.concept.base.images.ImageLoadRequest
import mozilla.components.concept.sync.DeviceType
import mozilla.components.support.ktx.kotlin.trimmed
import org.mozilla.fenix.R
@ -50,10 +54,13 @@ import org.mozilla.fenix.compose.button.SecondaryButton
import org.mozilla.fenix.home.recentsyncedtabs.RecentSyncedTab
import org.mozilla.fenix.theme.FirefoxTheme
private const val THUMBNAIL_SIZE = 108
/**
* A recent synced tab card.
*
* @param tab The [RecentSyncedTab] to display.
* @param storage [ThumbnailStorage] to obtain tab thumbnail bitmaps from.
* @param backgroundColor The background [Color] of the item.
* @param buttonBackgroundColor The background [Color] of the item's button.
* @param buttonTextColor The [Color] of the button's text.
@ -66,6 +73,7 @@ import org.mozilla.fenix.theme.FirefoxTheme
@Composable
fun RecentSyncedTab(
tab: RecentSyncedTab?,
storage: ThumbnailStorage,
backgroundColor: Color = FirefoxTheme.colors.layer2,
buttonBackgroundColor: Color = FirefoxTheme.colors.actionSecondary,
buttonTextColor: Color = FirefoxTheme.colors.textActionSecondary,
@ -109,7 +117,11 @@ fun RecentSyncedTab(
} else {
ThumbnailCard(
url = tab.url,
key = tab.url.hashCode().toString(),
request = ImageLoadRequest(
id = tab.url.hashCode().toString(),
size = LocalDensity.current.run { THUMBNAIL_SIZE.dp.toPx().toInt() },
),
storage = storage,
modifier = imageModifier,
)
}
@ -246,6 +258,7 @@ private fun LoadedRecentSyncedTab() {
FirefoxTheme {
RecentSyncedTab(
tab = tab,
storage = ThumbnailStorage(LocalContext.current),
onRecentSyncedTabClick = {},
onSeeAllSyncedTabsButtonClick = {},
onRemoveSyncedTab = {},
@ -259,6 +272,7 @@ private fun LoadingRecentSyncedTab() {
FirefoxTheme {
RecentSyncedTab(
tab = null,
storage = ThumbnailStorage(LocalContext.current),
buttonBackgroundColor = FirefoxTheme.colors.layer3,
onRecentSyncedTabClick = {},
onSeeAllSyncedTabsButtonClick = {},

@ -71,6 +71,7 @@ class RecentSyncedTabViewHolder(
RecentSyncedTab(
tab = syncedTab,
storage = components.core.thumbnailStorage,
backgroundColor = wallpaperState.wallpaperCardColor,
buttonBackgroundColor = buttonBackgroundColor,
buttonTextColor = buttonTextColor,

@ -48,6 +48,7 @@ class RecentTabViewHolder(
RecentTabs(
recentTabs = recentTabs.value ?: emptyList(),
storage = components.core.thumbnailStorage,
backgroundColor = wallpaperState.wallpaperCardColor,
onRecentTabClick = { recentTabInteractor.onRecentTabClicked(it) },
menuItems = listOf(

@ -39,6 +39,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.semantics.testTagsAsResourceId
@ -50,6 +52,7 @@ import mozilla.components.browser.icons.compose.Placeholder
import mozilla.components.browser.icons.compose.WithIcon
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.support.ktx.kotlin.trimmed
import mozilla.components.ui.colors.PhotonColors
import org.mozilla.fenix.components.components
@ -62,6 +65,8 @@ import org.mozilla.fenix.compose.inComposePreview
import org.mozilla.fenix.home.recenttabs.RecentTab
import org.mozilla.fenix.theme.FirefoxTheme
private const val THUMBNAIL_SIZE = 108
/**
* A list of recent tabs to jump back to.
*
@ -75,6 +80,7 @@ import org.mozilla.fenix.theme.FirefoxTheme
fun RecentTabs(
recentTabs: List<RecentTab>,
menuItems: List<RecentTabMenuItem>,
storage: ThumbnailStorage,
backgroundColor: Color = FirefoxTheme.colors.layer2,
onRecentTabClick: (String) -> Unit = {},
) {
@ -92,6 +98,7 @@ fun RecentTabs(
is RecentTab.Tab -> {
RecentTabItem(
tab = tab,
storage = storage,
menuItems = menuItems,
backgroundColor = backgroundColor,
onRecentTabClick = onRecentTabClick,
@ -117,6 +124,7 @@ fun RecentTabs(
@Suppress("LongMethod")
private fun RecentTabItem(
tab: RecentTab.Tab,
storage: ThumbnailStorage,
menuItems: List<RecentTabMenuItem>,
backgroundColor: Color,
onRecentTabClick: (String) -> Unit = {},
@ -141,6 +149,7 @@ private fun RecentTabItem(
) {
RecentTabImage(
tab = tab,
storage = storage,
modifier = Modifier
.size(108.dp, 80.dp)
.clip(RoundedCornerShape(8.dp)),
@ -214,6 +223,7 @@ private fun RecentTabItem(
@Composable
fun RecentTabImage(
tab: RecentTab.Tab,
storage: ThumbnailStorage,
modifier: Modifier = Modifier,
contentScale: ContentScale = ContentScale.FillWidth,
) {
@ -224,12 +234,14 @@ fun RecentTabImage(
Image(
url = previewImageUrl,
modifier = modifier,
targetSize = 108.dp,
targetSize = THUMBNAIL_SIZE.dp,
contentScale = ContentScale.Crop,
)
}
else -> TabThumbnail(
tab = tab.state,
size = LocalDensity.current.run { THUMBNAIL_SIZE.dp.toPx().toInt() },
storage = storage,
modifier = modifier,
contentScale = contentScale,
)
@ -320,6 +332,7 @@ private fun RecentTabsPreview() {
recentTabs = listOf(
tab,
),
storage = ThumbnailStorage(LocalContext.current),
menuItems = listOf(
RecentTabMenuItem(
title = "Menu item",

@ -27,6 +27,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
@ -36,6 +37,7 @@ import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.storage.sync.TabEntry
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.R
import org.mozilla.fenix.components.AppStore
@ -55,6 +57,7 @@ import mozilla.components.browser.storage.sync.Tab as SyncTab
* @param appStore [AppStore] used to listen for changes to [AppState].
* @param browserStore [BrowserStore] used to listen for changes to [BrowserState].
* @param tabsTrayStore [TabsTrayStore] used to listen for changes to [TabsTrayState].
* @param storage [ThumbnailStorage] to obtain tab thumbnail bitmaps from.
* @param displayTabsInGrid Whether the normal and private tabs should be displayed in a grid.
* @param isInDebugMode True for debug variant or if secret menu is enabled for this session.
* @param shouldShowTabAutoCloseBanner Whether the tab auto closer banner should be displayed.
@ -98,6 +101,7 @@ fun TabsTray(
appStore: AppStore,
browserStore: BrowserStore,
tabsTrayStore: TabsTrayStore,
storage: ThumbnailStorage,
displayTabsInGrid: Boolean,
isInDebugMode: Boolean,
shouldShowTabAutoCloseBanner: Boolean,
@ -193,6 +197,7 @@ fun TabsTray(
appStore = appStore,
browserStore = browserStore,
tabsTrayStore = tabsTrayStore,
storage = storage,
displayTabsInGrid = displayTabsInGrid,
onTabClose = onTabClose,
onTabMediaClick = onTabMediaClick,
@ -213,6 +218,7 @@ fun TabsTray(
PrivateTabsPage(
browserStore = browserStore,
tabsTrayStore = tabsTrayStore,
storage = storage,
displayTabsInGrid = displayTabsInGrid,
onTabClose = onTabClose,
onTabMediaClick = onTabMediaClick,
@ -239,6 +245,7 @@ private fun NormalTabsPage(
appStore: AppStore,
browserStore: BrowserStore,
tabsTrayStore: TabsTrayStore,
storage: ThumbnailStorage,
displayTabsInGrid: Boolean,
onTabClose: (TabSessionState) -> Unit,
onTabMediaClick: (TabSessionState) -> Unit,
@ -299,6 +306,7 @@ private fun NormalTabsPage(
TabLayout(
tabs = normalTabs,
storage = storage,
displayTabsInGrid = displayTabsInGrid,
selectedTabId = selectedTabId,
selectionMode = selectionMode,
@ -319,6 +327,7 @@ private fun NormalTabsPage(
private fun PrivateTabsPage(
browserStore: BrowserStore,
tabsTrayStore: TabsTrayStore,
storage: ThumbnailStorage,
displayTabsInGrid: Boolean,
onTabClose: (TabSessionState) -> Unit,
onTabMediaClick: (TabSessionState) -> Unit,
@ -335,6 +344,7 @@ private fun PrivateTabsPage(
if (privateTabs.isNotEmpty()) {
TabLayout(
tabs = privateTabs,
storage = storage,
displayTabsInGrid = displayTabsInGrid,
selectedTabId = selectedTabId,
selectionMode = selectionMode,
@ -508,6 +518,7 @@ private fun TabsTrayPreviewRoot(
appStore = appStore,
browserStore = browserStore,
tabsTrayStore = tabsTrayStore,
storage = ThumbnailStorage(LocalContext.current),
displayTabsInGrid = displayTabsInGrid,
isInDebugMode = false,
shouldShowInactiveTabsAutoCloseDialog = { true },

@ -240,6 +240,7 @@ class TabsTrayFragment : AppCompatDialogFragment() {
appStore = requireComponents.appStore,
browserStore = requireComponents.core.store,
tabsTrayStore = tabsTrayStore,
storage = requireComponents.core.thumbnailStorage,
displayTabsInGrid = requireContext().settings().gridTabView,
isInDebugMode = Config.channel.isDebug ||
requireComponents.settings.showSecretDebugMenuThisSession,

@ -21,25 +21,30 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import mozilla.components.browser.state.state.ContentState
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.compose.tabstray.TabGridItem
import org.mozilla.fenix.compose.tabstray.TabListItem
import org.mozilla.fenix.tabstray.ext.MIN_COLUMN_WIDTH_DP
import org.mozilla.fenix.theme.FirefoxTheme
import kotlin.math.max
/**
* Top-level UI for displaying a list of tabs.
*
* @param tabs The list of [TabSessionState] to display.
* @param storage [ThumbnailStorage] to obtain tab thumbnail bitmaps from.
* @param displayTabsInGrid Whether the tabs should be displayed in a grid.
* @param selectedTabId The ID of the currently selected tab.
* @param selectionMode [TabsTrayState.Mode] indicating whether the Tabs Tray is in single selection
* or multi-selection and contains the set of selected tabs.
* @param modifier
* @param onTabClose Invoked when the user clicks to close a tab.
* @param onTabMediaClick Invoked when the user interacts with a tab's media controls.
* @param onTabClick Invoked when the user clicks on a tab.
@ -50,6 +55,7 @@ import org.mozilla.fenix.theme.FirefoxTheme
@Composable
fun TabLayout(
tabs: List<TabSessionState>,
storage: ThumbnailStorage,
displayTabsInGrid: Boolean,
selectedTabId: String?,
selectionMode: TabsTrayState.Mode,
@ -72,6 +78,7 @@ fun TabLayout(
if (displayTabsInGrid) {
TabGrid(
tabs = tabs,
storage = storage,
selectedTabId = selectedTabId,
selectedTabIndex = selectedTabIndex,
selectionMode = selectionMode,
@ -85,6 +92,7 @@ fun TabLayout(
} else {
TabList(
tabs = tabs,
storage = storage,
selectedTabId = selectedTabId,
selectedTabIndex = selectedTabIndex,
selectionMode = selectionMode,
@ -102,6 +110,7 @@ fun TabLayout(
@Composable
private fun TabGrid(
tabs: List<TabSessionState>,
storage: ThumbnailStorage,
selectedTabId: String?,
selectedTabIndex: Int,
selectionMode: TabsTrayState.Mode,
@ -114,6 +123,10 @@ private fun TabGrid(
) {
val state = rememberLazyGridState(initialFirstVisibleItemIndex = selectedTabIndex)
val tabListBottomPadding = dimensionResource(id = R.dimen.tab_tray_list_bottom_padding)
val tabThumbnailSize = max(
LocalContext.current.resources.getDimensionPixelSize(R.dimen.tab_tray_grid_item_thumbnail_height),
LocalContext.current.resources.getDimensionPixelSize(R.dimen.tab_tray_grid_item_thumbnail_width),
)
val isInMultiSelectMode = selectionMode is TabsTrayState.Mode.Select
LazyVerticalGrid(
@ -133,6 +146,8 @@ private fun TabGrid(
) { tab ->
TabGridItem(
tab = tab,
thumbnailSize = tabThumbnailSize,
storage = storage,
isSelected = tab.id == selectedTabId,
multiSelectionEnabled = isInMultiSelectMode,
multiSelectionSelected = selectionMode.selectedTabs.contains(tab),
@ -153,6 +168,7 @@ private fun TabGrid(
@Composable
private fun TabList(
tabs: List<TabSessionState>,
storage: ThumbnailStorage,
selectedTabId: String?,
selectedTabIndex: Int,
selectionMode: TabsTrayState.Mode,
@ -165,6 +181,10 @@ private fun TabList(
) {
val state = rememberLazyListState(initialFirstVisibleItemIndex = selectedTabIndex)
val tabListBottomPadding = dimensionResource(id = R.dimen.tab_tray_list_bottom_padding)
val tabThumbnailSize = max(
LocalContext.current.resources.getDimensionPixelSize(R.dimen.tab_tray_list_item_thumbnail_height),
LocalContext.current.resources.getDimensionPixelSize(R.dimen.tab_tray_list_item_thumbnail_width),
)
val isInMultiSelectMode = selectionMode is TabsTrayState.Mode.Select
LazyColumn(
@ -183,6 +203,8 @@ private fun TabList(
) { tab ->
TabListItem(
tab = tab,
thumbnailSize = tabThumbnailSize,
storage = storage,
isSelected = tab.id == selectedTabId,
multiSelectionEnabled = isInMultiSelectMode,
multiSelectionSelected = selectionMode.selectedTabs.contains(tab),
@ -212,6 +234,7 @@ private fun TabListPreview() {
) {
TabLayout(
tabs = tabs,
storage = ThumbnailStorage(LocalContext.current),
selectedTabId = tabs[1].id,
selectionMode = TabsTrayState.Mode.Normal,
displayTabsInGrid = false,
@ -237,6 +260,7 @@ private fun TabGridPreview() {
) {
TabLayout(
tabs = tabs,
storage = ThumbnailStorage(LocalContext.current),
selectedTabId = tabs[0].id,
selectionMode = TabsTrayState.Mode.Normal,
displayTabsInGrid = true,
@ -264,6 +288,7 @@ private fun TabGridMultiSelectPreview() {
) {
TabLayout(
tabs = tabs,
storage = ThumbnailStorage(LocalContext.current),
selectedTabId = tabs[0].id,
selectionMode = TabsTrayState.Mode.Select(selectedTabs.toSet()),
displayTabsInGrid = false,

@ -15,6 +15,7 @@ import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.tabstray.TabsTray
import mozilla.components.browser.tabstray.TabsTrayStyling
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.components.components
import org.mozilla.fenix.compose.tabstray.TabGridItem
import org.mozilla.fenix.tabstray.TabsTrayInteractor
import org.mozilla.fenix.tabstray.TabsTrayState
@ -82,6 +83,8 @@ class ComposeGridViewHolder(
TabGridItem(
tab = tab,
thumbnailSize = 108,
storage = components.core.thumbnailStorage,
isSelected = isSelectedTab,
multiSelectionEnabled = multiSelectionEnabled,
multiSelectionSelected = isMultiSelectionSelected,

@ -15,6 +15,7 @@ import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.tabstray.TabsTray
import mozilla.components.browser.tabstray.TabsTrayStyling
import mozilla.components.lib.state.ext.observeAsComposableState
import org.mozilla.fenix.components.components
import org.mozilla.fenix.compose.tabstray.TabListItem
import org.mozilla.fenix.tabstray.TabsTrayInteractor
import org.mozilla.fenix.tabstray.TabsTrayState
@ -86,6 +87,8 @@ class ComposeListViewHolder(
TabListItem(
tab = tab,
thumbnailSize = 108,
storage = components.core.thumbnailStorage,
isSelected = isSelectedTabState,
multiSelectionEnabled = multiSelectionEnabled,
multiSelectionSelected = multiSelectionSelected,

Loading…
Cancel
Save