Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ANDROID-15405 -- PosterCard component #398

Merged
merged 40 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
adb61a7
dhonti/ANDROID-15405-postercard: First approach "PosterCard" component
dhonti-axpe Nov 19, 2024
e2c7a55
dhonti/ANDROID-15405: Update approach in order to accept images
dhonti-axpe Nov 19, 2024
45088ea
dhonti/ANDROID-15405-postercard: Fix viewing correctly lower custom c…
dhonti-axpe Nov 20, 2024
4402233
dhonti/ANDROID-15405-postercard: Integration new "TopActions" attributos
dhonti-axpe Nov 20, 2024
ff2a984
dhonti/ANDROID-15405-postercard: Removing "Video" type from current "…
dhonti-axpe Nov 21, 2024
f43a07f
dhonti/ANDROID-15405-postercard: Entry "PosterCard" creation in Misti…
dhonti-axpe Nov 21, 2024
7eeef69
dhonti/ANDROID-15405-postercard: Add sample background
dhonti-axpe Nov 21, 2024
8166136
dhonti/ANDROID-15405-postercard: Update components "TopAction" compon…
dhonti-axpe Nov 21, 2024
c74285e
dhonti/ANDROID-15405-postercard: Update some components
dhonti-axpe Nov 22, 2024
78a3947
dhonti/ANDROID-15405-postercard: Update sample attributes
dhonti-axpe Nov 22, 2024
85958e2
dhonti/NADROID-15405-postercard:
dhonti-axpe Nov 24, 2024
ad8a3ad
dhonti/ANDROID-15405-postercard: FIXED rescale when is provided an fi…
dhonti-axpe Nov 25, 2024
7ffe1aa
dhonti/ANDROID-15405-postercard: Place correctly the PosterCardTopAct…
dhonti-axpe Nov 25, 2024
e775585
dhonti/ANDROID-15405-postercard: Remove unused import
dhonti-axpe Nov 25, 2024
528c8cf
dhonti/ANDROID-15405-postercard: Some code corrections
dhonti-axpe Nov 25, 2024
2d4f90c
dhonti/ANDROID-15405-postercard: Create README.md file in "postercard…
dhonti-axpe Nov 25, 2024
d877419
dhonti/ANDROID-15405-postercard: Fixed linking to "compose" folder
dhonti-axpe Nov 25, 2024
af8fb30
dhonti/ANDRID-15405-postercard: Update location "sample_background"
dhonti-axpe Nov 25, 2024
cea0eb5
dhonti/ANDROID-15405-postercard: Update CONTRIBUTING.md
dhonti-axpe Nov 25, 2024
6c1f09a
dhonti/ANDROID-15405-postercard: Update PosterCard README file
dhonti-axpe Nov 25, 2024
e1fbfc4
dhonti/ANDROID-15405-postercard: Applied some corrections proposed by…
dhonti-axpe Nov 25, 2024
91ca854
dhonti/ANDROID-15405-postercard: Update modifier function in order to…
dhonti-axpe Nov 25, 2024
97a3b69
dhonti/ANDROID-15405-postercard: Update clickable() in modifier action
dhonti-axpe Nov 25, 2024
3586a36
dhonti/ANDROID-15405-postercard: New way to manage top actions list p…
dhonti-axpe Nov 26, 2024
573a304
dhonti/ANDROID-15405-postercard: Update corresponding README.md poste…
dhonti-axpe Nov 26, 2024
faf034f
dhonti/ANDROID-15405-postercard: Update use the "cardContentOverlay" …
dhonti-axpe Nov 27, 2024
ef9ca70
dhonti/ANDROID-15405-postercard:
dhonti-axpe Nov 27, 2024
f1f1d21
dhonti/ANDROID-15405-postercard: Update top actions management in code
dhonti-axpe Nov 27, 2024
2d4ce56
dhonti/ANDROID-15405-postercard: Update resource to catalog and chang…
dhonti-axpe Nov 28, 2024
6b3637a
dhonti/ANDROID-15405-postercard: New preview reference with no images
dhonti-axpe Nov 28, 2024
4c400f5
dhont/ANDROID-15405-postercard: Update correct reference to resource …
dhonti-axpe Nov 28, 2024
f73ec02
dhonti/ANDROID-15405-postercard: Update R mistica Catalog module in P…
dhonti-axpe Nov 28, 2024
2c44779
dhonti/ANDROID-15405-postercard: 1st approach for accesibility Poster…
dhonti-axpe Nov 29, 2024
bdea242
dhonti/ANDROID-15405-postercard: Remove unused imports
dhonti-axpe Nov 29, 2024
a3f85e6
dhonti/ANDROID-15405-postercard: Change colors to support dark mode t…
dhonti-axpe Nov 29, 2024
1d90eb7
dhonti/ANDROID-15405-postercard: 2nd approach a11 postercard
dhonti-axpe Nov 29, 2024
52bb0b9
dhonti/ANDROID-15405-postercard: First approach with a11 integrated
dhonti-axpe Dec 2, 2024
3ada235
dhonti/ANDROID-15405-postercard: A11 approach using "Modifier.zIndex(…
dhonti-axpe Dec 2, 2024
f78acfa
dhonti/ANDROID-15405-postercard: Apply some corrections from PR @jepr…
dhonti-axpe Dec 2, 2024
1686468
Merge remote-tracking branch 'origin/main' into dhonti/ANDROID-15405-…
dhonti-axpe Dec 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading