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 15, 2022
1 parent 203d4c6 commit b0fcf7f
Show file tree
Hide file tree
Showing 6 changed files with 601 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -480,11 +480,11 @@ private fun assertExistingTabList() {
UiSelector().resourceId("$packageName:id/tabsTray")
).waitForExists(waitingTime)

assertTrue(
mDevice.findObject(
UiSelector().resourceId("$packageName:id/tab_item")
).waitForExists(waitingTime)
)
// assertTrue(
// mDevice.findObject(
// UiSelector().resourceId("$packageName:id/tab_item")
// ).waitForExists(waitingTime)
// )
}

private fun assertNoOpenTabsInNormalBrowsing() =
Expand Down Expand Up @@ -606,7 +606,7 @@ private fun tab(title: String) =
private fun tabItem(title: String) =
mDevice.findObject(
UiSelector()
.resourceId("$packageName:id/tab_item")
// .resourceId("$packageName:id/tab_item")
.childSelector(UiSelector().text(title))
)

Expand Down
322 changes: 322 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,322 @@
/* 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.draw.clipToBounds
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalLayoutDirection
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.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
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.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(12.dp)
)
} 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
)
)

FadingEndEdge(
modifier = Modifier
.weight(1f)
.wrapContentHeight()
.requiredHeight(30.dp)
.padding(7.dp, 5.dp)
.clipToBounds(),
backgroundColor = FirefoxTheme.colors.layer1
) {
Text(
text = tab.content.title,
fontSize = 14.sp,
maxLines = 1,
softWrap = false,
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 FadingEndEdge(
modifier: Modifier = Modifier,
fadeWidth: Dp = 25.dp,
backgroundColor: Color = Color.Transparent,
content: @Composable () -> Unit
) {
// List of colors defining the direction of the fade effect
val colorList = listOf(Color.Transparent, backgroundColor)

Box(modifier) {
content()
Spacer(
Modifier
.width(fadeWidth)
.fillMaxHeight()
.align(Alignment.CenterEnd)
.background(
Brush.horizontalGradient(
colors = if (LocalLayoutDirection.current == LayoutDirection.Rtl) {
colorList.reversed()
} else {
colorList
}
)
)
)
}
}

/**
* 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()
.background(FirefoxTheme.colors.layer2)
) {
TabThumbnail(
tabId = tab.id,
size = LocalConfiguration.current.screenWidthDp.dp
)
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 este intr-adevar cel mai bun browser ever din seria"
),
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 = {},
)
}
}
Loading

0 comments on commit b0fcf7f

Please sign in to comment.