Skip to content

Commit

Permalink
For mozilla-mobile#21894: Move Tabs Tray to compose: Individual tab v…
Browse files Browse the repository at this point in the history
…iewholders: ListViewHolder.
  • Loading branch information
Amejia481 committed May 18, 2022
1 parent 922af02 commit 35bf2d1
Show file tree
Hide file tree
Showing 17 changed files with 729 additions and 89 deletions.
4 changes: 4 additions & 0 deletions app/src/main/java/org/mozilla/fenix/FeatureFlags.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,8 @@ object FeatureFlags {
* Enables receiving from the messaging framework.
*/
const val messagingFeature = true
/**
* Enables compose on the tabs tray items.
*/
val composeTabsTray = Config.channel.isDebug
}
78 changes: 45 additions & 33 deletions app/src/main/java/org/mozilla/fenix/compose/ThumbnailCard.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import mozilla.components.concept.base.images.ImageLoadRequest
import org.mozilla.fenix.R
import org.mozilla.fenix.components.components
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme

/**
* Card which will display a thumbnail. If a thumbnail is not available for [url], the favicon
Expand All @@ -40,6 +41,8 @@ import org.mozilla.fenix.theme.FirefoxTheme
* @param url Url to display thumbnail for.
* @param key Key used to remember the thumbnail for future compositions.
* @param modifier [Modifier] used to draw the image content.
* @param contentDescription Text used by accessibility services
* to describe what this image represents.
* @param contentScale [ContentScale] used to draw image content.
* @param alignment [Alignment] used to draw the image content.
*/
Expand All @@ -48,43 +51,50 @@ fun ThumbnailCard(
url: String,
key: String,
modifier: Modifier = Modifier,
contentDescription: String? = null,
contentScale: ContentScale = ContentScale.FillWidth,
alignment: Alignment = Alignment.TopCenter
) {
Card(
modifier = modifier,
backgroundColor = colorResource(id = R.color.photonGrey20)
) {
components.core.icons.Loader(url) {
Placeholder {
Box(
modifier = Modifier.background(color = FirefoxTheme.colors.layer3)
)
}

WithIcon { icon ->
Box(
modifier = Modifier.size(36.dp),
contentAlignment = Alignment.Center
) {
Image(
painter = icon.painter,
contentDescription = null,
modifier = Modifier
.size(36.dp)
.clip(RoundedCornerShape(8.dp)),
contentScale = ContentScale.Fit
if (inComposePreview) {
Box(
modifier = Modifier.background(color = FirefoxTheme.colors.layer3)
)
} else {
components.core.icons.Loader(url) {
Placeholder {
Box(
modifier = Modifier.background(color = FirefoxTheme.colors.layer3)
)
}

WithIcon { icon ->
Box(
modifier = Modifier.size(36.dp),
contentAlignment = Alignment.Center
) {
Image(
painter = icon.painter,
contentDescription = contentDescription,
modifier = Modifier
.size(36.dp)
.clip(RoundedCornerShape(8.dp)),
contentScale = ContentScale.Fit
)
}
}
}
}

ThumbnailImage(
key = key,
modifier = modifier,
contentScale = contentScale,
alignment = alignment
)
ThumbnailImage(
key = key,
modifier = modifier,
contentScale = contentScale,
alignment = alignment
)
}
}
}

Expand Down Expand Up @@ -120,11 +130,13 @@ private fun ThumbnailImage(
@Preview
@Composable
private fun ThumbnailCardPreview() {
ThumbnailCard(
url = "https://mozilla.com",
key = "123",
modifier = Modifier
.size(108.dp, 80.dp)
.clip(RoundedCornerShape(8.dp))
)
FirefoxTheme(theme = Theme.getTheme(isPrivate = false)) {
ThumbnailCard(
url = "https://mozilla.com",
key = "123",
modifier = Modifier
.size(108.dp, 80.dp)
.clip(RoundedCornerShape(8.dp))
)
}
}
83 changes: 83 additions & 0 deletions app/src/main/java/org/mozilla/fenix/compose/tabstray/MediaImage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/* 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 androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.state.createTab
import mozilla.components.concept.engine.mediasession.MediaSession.PlaybackState
import org.mozilla.fenix.R
import org.mozilla.fenix.theme.FirefoxTheme

/**
* Draws controller buttons for the media (play/pause) state of the given [tab].
*
* @param tab the tab which the image should be shown.
* @param onMediaIconClicked handles the click event when tab has media session like play/pause.
* @param modifier [Modifier] to be applied to the layout.
*/
@Composable
fun MediaImage(
tab: TabSessionState,
onMediaIconClicked: ((TabSessionState) -> Unit),
modifier: Modifier,
) {
val (icon, contentDescription) = when (tab.mediaSessionState?.playbackState) {
PlaybackState.PAUSED -> {
R.drawable.media_state_play_vector to R.string.mozac_feature_media_notification_action_play
}
PlaybackState.PLAYING -> {
R.drawable.media_state_pause_vector to R.string.mozac_feature_media_notification_action_pause
}
else -> return
}

Card(
modifier = modifier
.size(size = 24.dp)
.clip(shape = CircleShape)
.border(
width = 2.dp,
color = FirefoxTheme.colors.layer2,
shape = CircleShape
),
backgroundColor = FirefoxTheme.colors.layerAccent
) {
Icon(
painter = painterResource(id = icon),
modifier = Modifier
.size(size = 24.dp)
.clickable { onMediaIconClicked(tab) },
contentDescription = stringResource(contentDescription),
tint = FirefoxTheme.colors.borderPrimary
)
}
}

@Composable
@Preview
private fun ImagePreview() {
MediaImage(
tab = createTab(url = "https://mozilla.com"),
onMediaIconClicked = {},
modifier = Modifier
.height(100.dp)
.width(200.dp)
)
}
183 changes: 183 additions & 0 deletions app/src/main/java/org/mozilla/fenix/compose/tabstray/TabListItem.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/* 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 androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
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.PrimaryText
import org.mozilla.fenix.compose.SecondaryText
import org.mozilla.fenix.compose.ThumbnailCard
import org.mozilla.fenix.tabstray.ext.toShortUrl
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme

/**
* A composable that renders tab list layout.
*
* @param tab The given tab to be render as view a list 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")
fun TabListItem(
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 contentBackgroundColor = if (isSelected) {
FirefoxTheme.colors.layerAccentNonOpaque
} else {
FirefoxTheme.colors.layer1
}
Row(
modifier = Modifier
.fillMaxWidth()
.background(contentBackgroundColor)
.combinedClickable(
onLongClick = { onLongClick(tab) },
onClick = {
onClick(tab)
}
)
.padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Thumbnail(
tab = tab,
multiSelectionEnabled = multiSelectionEnabled,
isSelected = multiSelectionSelected,
onMediaIconClicked = { onMediaClick(it) }
)

Column(
modifier = Modifier
.padding(horizontal = 16.dp)
.weight(weight = 1f)
) {

PrimaryText(
text = tab.content.title,
fontSize = 16.sp,
maxLines = 2
)

SecondaryText(
text = tab.toShortUrl(),
fontSize = 12.sp,
)
}

if (!multiSelectionEnabled) {
IconButton(
onClick = { onCloseClick(tab) },
modifier = Modifier.size(size = 24.dp),
) {
Icon(
painter = painterResource(id = R.drawable.mozac_ic_close),
contentDescription = stringResource(
id = R.string.close_tab_title,
tab.content.title
),
tint = FirefoxTheme.colors.iconPrimary
)
}
}
}
}

@Composable
private fun Thumbnail(
tab: TabSessionState,
multiSelectionEnabled: Boolean,
isSelected: Boolean,
onMediaIconClicked: ((TabSessionState) -> Unit)
) {
Box {
ThumbnailCard(
url = tab.content.url,
key = tab.id,
modifier = Modifier.size(width = 92.dp, height = 72.dp),
contentDescription = stringResource(id = R.string.mozac_browser_tabstray_open_tab),
)

if (isSelected) {
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)
)
}
}
if (!multiSelectionEnabled) {
MediaImage(
tab = tab,
onMediaIconClicked = onMediaIconClicked,
modifier = Modifier.align(Alignment.TopEnd)
)
}
}
}

@Composable
@Preview
private fun TabListItemPreview() {
FirefoxTheme(theme = Theme.getTheme(isPrivate = false)) {
TabListItem(
tab = createTab(url = "www.mozilla.com", title = "Mozilla"),
onCloseClick = {},
onMediaClick = {},
onClick = {},
onLongClick = {},
)
}
}
Loading

0 comments on commit 35bf2d1

Please sign in to comment.