Skip to content

Commit

Permalink
ANDROID-15405 -- PosterCard component (#398)
Browse files Browse the repository at this point in the history
* dhonti/ANDROID-15405-postercard: First approach "PosterCard" component

* dhonti/ANDROID-15405: Update approach in order to accept images

* dhonti/ANDROID-15405-postercard: Fix viewing correctly lower custom content Composable and reorganizing current composables in package

* dhonti/ANDROID-15405-postercard: Integration new "TopActions" attributos

* dhonti/ANDROID-15405-postercard: Removing "Video" type from current "PosterCard" implementation. To be updated in future

* dhonti/ANDROID-15405-postercard: Entry "PosterCard" creation in Mistica Catalog app

* dhonti/ANDROID-15405-postercard: Add sample background

* dhonti/ANDROID-15405-postercard: Update components "TopAction" components in order to fit clickable actions

* dhonti/ANDROID-15405-postercard: Update some components

* dhonti/ANDROID-15405-postercard: Update sample attributes

* dhonti/NADROID-15405-postercard:
- Added param in "TopAction" component to be able to receive a "testTag".
- FIXED problem resize Image()

* dhonti/ANDROID-15405-postercard: FIXED rescale when is provided an fixed aspect ratio and the height inner content is bigger than aspect ratio height.

* dhonti/ANDROID-15405-postercard: Place correctly the PosterCardTopActions() subcomposable in order to be more visible

* dhonti/ANDROID-15405-postercard: Remove unused import

* dhonti/ANDROID-15405-postercard: Some code corrections

* dhonti/ANDROID-15405-postercard: Create README.md file in "postercard" folder and linking with main README.md library

* dhonti/ANDROID-15405-postercard: Fixed linking to "compose" folder

* dhonti/ANDRID-15405-postercard: Update location "sample_background"

* dhonti/ANDROID-15405-postercard: Update CONTRIBUTING.md

* dhonti/ANDROID-15405-postercard: Update PosterCard README file

* dhonti/ANDROID-15405-postercard: Applied some corrections proposed by @haynlo

* dhonti/ANDROID-15405-postercard: Update modifier function in order to accept a clickable() and update param "contentDescription"

* dhonti/ANDROID-15405-postercard: Update clickable() in modifier action

* dhonti/ANDROID-15405-postercard: New way to manage top actions list processing

* dhonti/ANDROID-15405-postercard: Update corresponding README.md postercard

* dhonti/ANDROID-15405-postercard: Update use the "cardContentOverlay" in background shadow main content text

* dhonti/ANDROID-15405-postercard:
- Update TopAction components background according to Mistica color values and make them clickables

* dhonti/ANDROID-15405-postercard: Update top actions management in code

* dhonti/ANDROID-15405-postercard: Update resource to catalog and change format to WEBP

* dhonti/ANDROID-15405-postercard: New preview reference with no images

* dhont/ANDROID-15405-postercard: Update correct reference to resource in PosterCard() main component

* dhonti/ANDROID-15405-postercard: Update R mistica Catalog module in PosterCards file

* dhonti/ANDROID-15405-postercard: 1st approach for accesibility PosterCard

* dhonti/ANDROID-15405-postercard: Remove unused imports

* dhonti/ANDROID-15405-postercard: Change colors to support dark mode top actions

* dhonti/ANDROID-15405-postercard: 2nd approach a11 postercard

* dhonti/ANDROID-15405-postercard: First approach with a11 integrated

* dhonti/ANDROID-15405-postercard: A11 approach using "Modifier.zIndex()" in corresponding components

* dhonti/ANDROID-15405-postercard: Apply some corrections from PR @jeprubio
  • Loading branch information
dhonti-axpe authored Dec 3, 2024
1 parent 300bada commit 36a19aa
Show file tree
Hide file tree
Showing 12 changed files with 751 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Don't hesitate to ask any questions and share your ideas
We would love to accept your Pull Requests but please, before starting your development,
[create an issue](https://github.com/Telefonica/mistica-android/issues/new/choose).

Include someone from **Telefonica/mistica-design** team as reviewer.

## Bug reports

If something is broken or not working as expected, let us know!
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Just set your App or any specific activity to use any of the following:
* [Load Error Feedback](library/src/main/java/com/telefonica/mistica/feedback/error)
* [Media Cards](library/src/main/java/com/telefonica/mistica/card/mediacard)
* [Pop Overs](library/src/main/java/com/telefonica/mistica/feedback/popover)
* [Poster Card](library/src/main/java/com/telefonica/mistica/compose/card/postercard)
* [Screen Feedbacks](library/src/main/java/com/telefonica/mistica/feedback/screen)
* [Scroll Content Indicator](library/src/main/java/com/telefonica/mistica/contentindicator)
* [Sheet](library/src/main/java/com/telefonica/mistica/sheet)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class CatalogMainActivity : AppCompatActivity() {
SectionItem("Lists", R.drawable.ic_lists, Section.LISTS),
SectionItem("Load Error Feedback", R.drawable.ic_load_feedback_error, Section.LOAD_ERROR_FEEDBACK),
SectionItem("Media Card", R.drawable.ic_cards, Section.MEDIA_CARDS),
SectionItem("Poster Card", R.drawable.ic_cards, Section.POSTER_CARD),
SectionItem("Filters", R.drawable.ic_filters, Section.FILTERS),
SectionItem("PopOvers", R.drawable.ic_popovers, Section.POPOVERS),
SectionItem("Scroll Content indicator", R.drawable.ic_feedbacks, Section.SCROLL_CONTENT_INDICATOR),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import com.telefonica.mistica.catalog.ui.compose.components.Lists
import com.telefonica.mistica.catalog.ui.compose.components.LoadErrorFeedbacks
import com.telefonica.mistica.catalog.ui.compose.components.MediaCards
import com.telefonica.mistica.catalog.ui.compose.components.PopOvers
import com.telefonica.mistica.catalog.ui.compose.components.PosterCards
import com.telefonica.mistica.catalog.ui.compose.components.Skeletons
import com.telefonica.mistica.catalog.ui.compose.components.Steppers
import com.telefonica.mistica.catalog.ui.compose.components.TabsCatalog
Expand Down Expand Up @@ -128,7 +129,8 @@ class ComponentCatalogActivity : AppCompatActivity() {
Section.CALLOUTS to ::setCalloutsCatalogFragment,
Section.SHEET to ::setSheetCatalogFragment,
Section.CAROUSEL to ::setCarouselFragment,
Section.SKELETON to ::setSkeletonFragment
Section.SKELETON to ::setSkeletonFragment,
Section.POSTER_CARD to ::setPosterCardFragment
)

val section = intent.getSerializableExtra(EXTRA_SECTION) as? Section
Expand Down Expand Up @@ -210,6 +212,12 @@ class ComponentCatalogActivity : AppCompatActivity() {
composeComponent = { DataCards() })
}

private fun setPosterCardFragment() {
setPageAdapterWithTabs(
classicComponent = null,
composeComponent = { PosterCards() })
}

private fun setMediaCardsFragment() {
setPageAdapterWithTabs(
classicComponent = MediaCardsFragment(),
Expand Down Expand Up @@ -398,5 +406,6 @@ enum class Section {
SHEET,
CAROUSEL,
SKELETON,
POSTER_CARD
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
package com.telefonica.mistica.catalog.ui.compose.components

import androidx.annotation.AttrRes
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Checkbox
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.graphics.SolidColor
import androidx.compose.ui.unit.dp
import com.telefonica.mistica.catalog.R
import com.telefonica.mistica.compose.card.postercard.PosterCard
import com.telefonica.mistica.compose.card.postercard.PosterCardAspectRatio
import com.telefonica.mistica.compose.card.postercard.PosterCardBackgroundType
import com.telefonica.mistica.compose.card.postercard.TopActionData
import com.telefonica.mistica.compose.input.DropDownInput
import com.telefonica.mistica.compose.tag.Tag
import com.telefonica.mistica.compose.theme.MisticaTheme
import com.telefonica.mistica.tag.TagView
import com.telefonica.mistica.tag.TagView.Companion.TYPE_PROMO

@Composable
fun PosterCards() {

var tag: String by remember { mutableStateOf("Headline") }
var tagType: Int by remember { mutableIntStateOf(TYPE_PROMO) }

var aspectRatioType: PosterCardAspectRatio by remember { mutableStateOf(PosterCardAspectRatio.AR_AUTO) }

var inverseDisplay: Boolean by remember { mutableStateOf(true) }
var backgroundType: BackgroundType by remember { mutableStateOf(BackgroundType.IMAGE) }

var topActionsType: TopActionsType by remember { mutableStateOf(TopActionsType.NONE) }

var preTitle: String by remember { mutableStateOf("Pretitle") }
var title: String by remember { mutableStateOf("Title") }
var subtitle: String by remember { mutableStateOf("Subtitle") }
var description: String by remember { mutableStateOf("Description") }

var withAdditionalContent: Boolean by remember { mutableStateOf(false) }


Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
DropDownInput(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp),
items = PosterCardAspectRatio.entries.map { aspectRatioLabelsMaps[it].orEmpty() },
currentItemIndex = PosterCardAspectRatio.entries.indexOf(aspectRatioType),
onItemSelected = { index -> aspectRatioType = PosterCardAspectRatio.entries.toTypedArray()[index] },
hint = "Aspect Ratio",
)

DropDownInput(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp),
items = BackgroundType.entries.map { backgroundTypeLabelsMap[it].orEmpty() },
currentItemIndex = BackgroundType.entries.indexOf(backgroundType),
onItemSelected = { index -> backgroundType = BackgroundType.entries.toTypedArray()[index] },
hint = "Background type",
)

if (backgroundType != BackgroundType.IMAGE) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Inverse display")
Checkbox(checked = inverseDisplay, onCheckedChange = {
inverseDisplay = !inverseDisplay
backgroundType.backgroundValue.inverseDisplay = inverseDisplay
})
}
}

DropDownInput(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp),
items = TopActionsType.entries.map { topActionsTypeLabelsMap[it].orEmpty() },
currentItemIndex = TopActionsType.entries.indexOf(topActionsType),
onItemSelected = { index -> topActionsType = TopActionsType.entries.toTypedArray()[index] },
hint = "Top actions",
)

OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = tag,
onValueChange = { tag = it }, label = { Text("Tag label") }
)

DropDownInput(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp),
items = TagColorsValues.entries.map { it.name },
currentItemIndex = tagType,
onItemSelected = { index -> tagType = index },
hint = "Tag style",
)

OutlinedTextField(modifier = Modifier.fillMaxWidth(), value = preTitle, onValueChange = { preTitle = it }, label = { Text("Pretitle") })
OutlinedTextField(modifier = Modifier.fillMaxWidth(), value = title, onValueChange = { title = it }, label = { Text("Title") })
OutlinedTextField(modifier = Modifier.fillMaxWidth(), value = subtitle, onValueChange = { subtitle = it }, label = { Text("Subtitle") })
OutlinedTextField(modifier = Modifier.fillMaxWidth(), value = description, onValueChange = { description = it }, label = { Text("Description") })

Row(verticalAlignment = Alignment.CenterVertically) {
Text("With additional content")
Checkbox(checked = withAdditionalContent, onCheckedChange = { withAdditionalContent = !withAdditionalContent })
}

PosterCard(
modifier = Modifier.fillMaxWidth(),
aspectRatio = aspectRatioType,
backgroundType = backgroundType.backgroundValue,
headline = if (tag.isNotEmpty()) Tag(tag).withStyle(tagType) else null,
preTitle = preTitle.getOrNullIfEmpty(),
title = title.getOrNullIfEmpty(),
subtitle = subtitle.getOrNullIfEmpty(),
description = description.getOrNullIfEmpty(),
firstTopAction = topActionsType.info?.firstTopAction,
secondTopAction = topActionsType.info?.secondTopAction,
customContent = {
if (withAdditionalContent) {
AdditionalContent()
}
}
)
}
}

private enum class TagColorsValues(@AttrRes val tagStyle: Int) {
PROMO(TYPE_PROMO),
ACTIVE(TagView.TYPE_ACTIVE),
INACTIVE(TagView.TYPE_INACTIVE),
SUCCESS(TagView.TYPE_SUCCESS),
WARNING(TagView.TYPE_WARNING),
ERROR(TagView.TYPE_ERROR),
INVERSE(TagView.TYPE_INVERSE),
}

private val aspectRatioLabelsMaps = mapOf(
PosterCardAspectRatio.AR_AUTO to "Auto",
PosterCardAspectRatio.AR_1_1 to "1:1",
PosterCardAspectRatio.AR_7_10 to "7:10",
PosterCardAspectRatio.AR_9_10 to "9:10",
PosterCardAspectRatio.AR_16_9 to "16:9"
)

private val backgroundTypeLabelsMap = mapOf(
BackgroundType.SOLID_COLOR to "Solid color",
BackgroundType.GRADIENT_COLOR to "Gradient color",
BackgroundType.IMAGE to "Image"
)

private val topActionsTypeLabelsMap = mapOf(
TopActionsType.NONE to "None",
TopActionsType.ONE_ACTION_DISMISS to "One action (Dismiss)",
TopActionsType.TWO_ACTIONS to "Two actions (View + Dismiss)"
)

private enum class BackgroundType(val backgroundValue: PosterCardBackgroundType) {
IMAGE(PosterCardBackgroundType.Image(imageResource = R.drawable.sample_background, contentDescription = "Mística PosterCard")),
SOLID_COLOR(PosterCardBackgroundType.Color(brush = SolidColor(Color.Red))),
GRADIENT_COLOR(PosterCardBackgroundType.Color(brush = Brush.verticalGradient(colors = listOf(Color.Blue, Color.Cyan)))),
}

private enum class TopActionsType(val info: PosterCardTopActionInfo? = null) {
NONE,
ONE_ACTION_DISMISS(
info = PosterCardTopActionInfo(
firstTopAction = TopActionData(iconRes = R.drawable.ic_close_regular)
)
),
TWO_ACTIONS(
info = PosterCardTopActionInfo(
firstTopAction = TopActionData(
iconRes = R.drawable.icn_visibility,
contentDescription = "PosterCard Visible top action"
),
secondTopAction = TopActionData(
iconRes = R.drawable.ic_close_regular,
contentDescription = "PosterCard Close top action"
)
)
)
}

private data class PosterCardTopActionInfo(
val firstTopAction: TopActionData? = null,
val secondTopAction: TopActionData? = null
)

@Composable
internal fun AdditionalContent() {
Box(
modifier = Modifier
.fillMaxWidth()
.size(150.dp)
.background(color = MisticaTheme.colors.successHighInverse.copy(alpha = 0.5f))
.border(width = 1.dp, color = MisticaTheme.colors.success),
contentAlignment = Alignment.Center
) {
Text(
text = "Additional content",
style = MisticaTheme.typography.preset2,
color = MisticaTheme.colors.textPrimaryInverse
)
}
}

private fun String.getOrNullIfEmpty(): String? = this.ifEmpty { null }
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.telefonica.mistica.compose.card.postercard

import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
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.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.telefonica.mistica.compose.tag.Tag
import com.telefonica.mistica.compose.theme.MisticaTheme

@Composable
fun PosterCard(
aspectRatio: PosterCardAspectRatio,
backgroundType: PosterCardBackgroundType,
modifier: Modifier = Modifier,
headline: Tag? = null,
preTitle: String? = null,
title: String? = null,
subtitle: String? = null,
description: String? = null,
firstTopAction: TopActionData? = null,
secondTopAction: TopActionData? = null,
onClickAction: (() -> Unit)? = null,
customContent: (@Composable () -> Unit)? = null,
) {
val anyTopActionsLoaded = firstTopAction != null || secondTopAction != null

BoxWithConstraints(modifier = modifier) {
androidx.compose.material.Card(
elevation = 0.dp,
shape = RoundedCornerShape(MisticaTheme.radius.containerBorderRadius),
modifier = Modifier
.width(maxWidth)
.clickable(enabled = onClickAction != null) {
onClickAction?.invoke()
}
.heightIn(
min = maxWidth / aspectRatio.ratio,
max = Dp.Infinity
)
) {
PosterCardBackground(backgroundType = backgroundType) {
Column(
modifier = Modifier.align(alignment = Alignment.BottomCenter),
verticalArrangement = Arrangement.Bottom
) {
if (anyTopActionsLoaded) {
Spacer(modifier = Modifier.height(40.dp))
}
PosterCardMainContent(
backgroundType = backgroundType,
tag = headline,
preTitle = preTitle,
title = title,
description = description,
subtitle = subtitle,
customContent = customContent
)
}
if (anyTopActionsLoaded) {
PosterCardTopActions(
modifier = Modifier.align(alignment = Alignment.TopCenter).zIndex(1f),
firstTopAction = firstTopAction,
secondTopAction = secondTopAction
)
}
}
}
}
}

enum class PosterCardAspectRatio(val ratio: Float) {
AR_AUTO(ratio = Float.NaN),
AR_1_1(ratio = 1f),
AR_7_10(ratio = 0.7f),
AR_9_10(ratio = 0.9f),
AR_16_9(16 / 9f)
}

sealed class PosterCardBackgroundType(open var inverseDisplay: Boolean) {
data class Image(@DrawableRes val imageResource: Int, val contentDescription: String = "") : PosterCardBackgroundType(inverseDisplay = true)
data class Color(val brush: Brush, override var inverseDisplay: Boolean = true) : PosterCardBackgroundType(inverseDisplay = inverseDisplay)
}

Loading

0 comments on commit 36a19aa

Please sign in to comment.