Skip to content

Commit

Permalink
For mozilla-mobile#25891 - Move GridViewHolder to compose
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexandru2909 committed Jul 12, 2022
1 parent 203d4c6 commit 8ec56c1
Show file tree
Hide file tree
Showing 4 changed files with 467 additions and 6 deletions.
314 changes: 314 additions & 0 deletions app/src/main/java/org/mozilla/fenix/compose/tabstray/TabGridItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
/* 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.tabstray

import android.content.res.Configuration
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.combinedClickable
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.fillMaxHeight
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.requiredHeight
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
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.dp
import androidx.compose.ui.unit.sp
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.state.createTab
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.Favicon
import org.mozilla.fenix.compose.ThumbnailCard
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme

/**
* Tab grid item used to display a tab that supports clicks,
* long clicks, multiple selection, and media controls.
*
* @param tab The given tab to be render as view a grid item.
* @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.
* @param multiSelectionSelected Indicates if the item should be render as multi selection selected
* option.
* @param onCloseClick Callback to handle the click event of the close button.
* @param onMediaClick Callback to handle when the media item is clicked.
* @param onClick Callback to handle when item is clicked.
* @param onLongClick Callback to handle when item is long clicked.
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
@Suppress("MagicNumber", "LongParameterList", "LongMethod")
fun TabGridItem(
tab: TabSessionState,
isSelected: Boolean = false,
multiSelectionEnabled: Boolean = false,
multiSelectionSelected: Boolean = false,
onCloseClick: (tab: TabSessionState) -> Unit,
onMediaClick: (tab: TabSessionState) -> Unit,
onClick: (tab: TabSessionState) -> Unit,
onLongClick: (tab: TabSessionState) -> Unit,
) {
val tabBorderModifier = if (isSelected && !multiSelectionEnabled) {
Modifier.border(
4.dp,
FirefoxTheme.colors.borderAccent,
RoundedCornerShape(dimensionResource(id = R.dimen.tab_tray_grid_item_outer_border_radius))
)
} else {
Modifier
}

Box(
modifier = Modifier
.wrapContentHeight()
.wrapContentWidth()
) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(202.dp)
.padding(4.dp)
.then(tabBorderModifier)
.padding(4.dp)
.combinedClickable(
onLongClick = { onLongClick(tab) },
onClick = { onClick(tab) }
),
elevation = 0.dp,
shape = RoundedCornerShape(dimensionResource(id = R.dimen.tab_tray_grid_item_border_radius)),
border = BorderStroke(1.dp, FirefoxTheme.colors.borderPrimary)
) {

Column(
modifier = Modifier.background(FirefoxTheme.colors.layer1)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
) {
Favicon(
url = tab.content.url,
size = 16.dp,
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(
8.dp, 0.dp, 0.dp, 0.dp
)
)

FadingEdgeRight(
modifier = Modifier
.weight(1f)
.wrapContentHeight()
.requiredHeight(30.dp)
.padding(7.dp, 5.dp),
backgroundColor = FirefoxTheme.colors.layer1
) {
Text(
text = tab.content.title,
fontSize = 14.sp,
maxLines = 1,
overflow = TextOverflow.Visible,
style = TextStyle(color = FirefoxTheme.colors.textPrimary),
)
}

IconButton(
onClick = { onCloseClick(tab) },
modifier = Modifier
.size(size = 24.dp)
.align(Alignment.CenterVertically)

) {
Icon(
painter = painterResource(id = R.drawable.mozac_ic_close),
contentDescription = stringResource(id = R.string.close_tab),
tint = FirefoxTheme.colors.iconPrimary,
)
}
}
Divider(
color = FirefoxTheme.colors.borderPrimary,
thickness = 1.dp
)

Thumbnail(
tab = tab,
multiSelectionSelected = multiSelectionSelected,
)
}
}

MediaImage(
tab = tab,
onMediaIconClicked = { onMediaClick(tab) },
modifier = Modifier
.align(Alignment.TopStart)
)
}
}

/**
* Displays the [content] with the right edge fading.
*
* @param modifier [Modifier] for the container.
* @param fadeWidth Length of the fading edge.
* @param backgroundColor Color of the background shown under the content.
* @param content The content whose right edge must be faded.
*/
@Composable
private fun FadingEdgeRight(
modifier: Modifier = Modifier,
fadeWidth: Dp = 25.dp,
backgroundColor: Color = Color.Transparent,
content: @Composable () -> Unit
) {
Box(modifier) {
content()
Spacer(
Modifier
.width(fadeWidth)
.fillMaxHeight()
.align(Alignment.CenterEnd)
.background(
Brush.horizontalGradient(
colors = listOf(
Color.Transparent,
backgroundColor
)
)
)
)
}
}

/**
* Thumbnail specific for the [TabGridItem], which can be selected.
*
* @param tab Tab, containing the thumbnail to be displayed.
* @param multiSelectionSelected Whether or not the multiple selection is enabled.
*/
@Composable
private fun Thumbnail(
tab: TabSessionState,
multiSelectionSelected: Boolean,
) {
Box(
modifier = Modifier
.fillMaxSize()
) {
ThumbnailCard(
url = tab.content.url,
key = tab.id,
modifier = Modifier.fillMaxSize(),
contentDescription = stringResource(id = R.string.mozac_browser_tabstray_open_tab),
)

if (multiSelectionSelected) {
Box(
modifier = Modifier
.fillMaxSize()
.background(FirefoxTheme.colors.layerAccentNonOpaque)
)
Card(
modifier = Modifier
.size(size = 40.dp)
.align(alignment = Alignment.Center),
shape = CircleShape,
backgroundColor = FirefoxTheme.colors.layerAccent,
) {
Icon(
painter = painterResource(id = R.drawable.mozac_ic_check),
modifier = Modifier
.matchParentSize()
.padding(all = 8.dp),
contentDescription = null,
tint = colorResource(id = R.color.mozac_ui_icons_fill)
)
}
}
}
}

@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun TabGridItemPreview() {
FirefoxTheme(theme = Theme.getTheme()) {
TabGridItem(
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
onCloseClick = {},
onMediaClick = {},
onClick = {},
onLongClick = {},
)
}
}

@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun TabGridItemSelectedPreview() {
FirefoxTheme(theme = Theme.getTheme()) {
TabGridItem(
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
isSelected = true,
onCloseClick = {},
onMediaClick = {},
onClick = {},
onLongClick = {},
)
}
}

@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
private fun TabGridItemMultiSelectedPreview() {
FirefoxTheme(theme = Theme.getTheme()) {
TabGridItem(
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
multiSelectionEnabled = true,
multiSelectionSelected = true,
onCloseClick = {},
onMediaClick = {},
onClick = {},
onLongClick = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.mozilla.fenix.databinding.TabTrayItemBinding
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.selection.SelectionHolder
import org.mozilla.fenix.tabstray.TabsTrayStore
import org.mozilla.fenix.tabstray.browser.compose.ComposeGridViewHolder
import org.mozilla.fenix.tabstray.browser.compose.ComposeListViewHolder

/**
Expand All @@ -48,7 +49,8 @@ class BrowserTabsAdapter(
enum class ViewType(val layoutRes: Int) {
LIST(BrowserTabViewHolder.ListViewHolder.LAYOUT_ID),
COMPOSE_LIST(ComposeListViewHolder.LAYOUT_ID),
GRID(BrowserTabViewHolder.GridViewHolder.LAYOUT_ID)
GRID(BrowserTabViewHolder.GridViewHolder.LAYOUT_ID),
COMPOSE_GRID(ComposeGridViewHolder.LAYOUT_ID)
}

/**
Expand All @@ -62,7 +64,11 @@ class BrowserTabsAdapter(
override fun getItemViewType(position: Int): Int {
return when {
context.components.settings.gridTabView -> {
ViewType.GRID.layoutRes
if (FeatureFlags.composeTabsTray) {
ViewType.COMPOSE_GRID.layoutRes
} else {
ViewType.GRID.layoutRes
}
}
else -> {
if (FeatureFlags.composeTabsTray) {
Expand All @@ -85,6 +91,15 @@ class BrowserTabsAdapter(
featureName = featureName,
viewLifecycleOwner = viewLifecycleOwner
)
ViewType.COMPOSE_GRID.layoutRes ->
ComposeGridViewHolder(
interactor = interactor,
store = store,
selectionHolder = selectionHolder,
composeItemView = ComposeView(parent.context),
featureName = featureName,
viewLifecycleOwner = viewLifecycleOwner
)
else -> {
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
if (viewType == ViewType.GRID.layoutRes) {
Expand Down
Loading

0 comments on commit 8ec56c1

Please sign in to comment.