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

Update Uamp Player Screen bottom buttons layout #2407

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
6 changes: 4 additions & 2 deletions composables/api/current.api
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ package com.google.android.horologist.composables {
}

public final class MarqueeTextKt {
method @androidx.compose.runtime.Composable @com.google.android.horologist.annotations.ExperimentalHorologistApi public static void MarqueeText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional long color, optional androidx.compose.ui.text.TextStyle style, optional int textAlign, optional float followGap, optional float edgeGradientWidth, optional float marqueeDpPerSecond, optional long pauseTime);
method @androidx.compose.runtime.Composable @com.google.android.horologist.annotations.ExperimentalHorologistApi public static void MarqueeText(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional androidx.compose.ui.text.TextStyle style, optional int textAlign, optional float followGap, optional float edgeGradientWidth, optional float marqueeDpPerSecond, optional long pauseTime);
method @androidx.compose.runtime.Composable @com.google.android.horologist.annotations.ExperimentalHorologistApi public static void MarqueeText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional long color, optional androidx.compose.ui.text.TextStyle style, optional int textAlign, optional float followGap, optional float startGap, optional float startEdgeGradientWidth, optional float endEdgeGradientWidth, optional float marqueeDpPerSecond, optional long pauseTime);
method @androidx.compose.runtime.Composable @com.google.android.horologist.annotations.ExperimentalHorologistApi public static void MarqueeText(androidx.compose.ui.text.AnnotatedString text, optional androidx.compose.ui.Modifier modifier, optional java.util.Map<java.lang.String,androidx.compose.foundation.text.InlineTextContent> inlineContent, optional long color, optional androidx.compose.ui.text.TextStyle style, optional int textAlign, optional float followGap, optional float startGap, optional float edgeGradientWidth, optional float marqueeDpPerSecond, optional long pauseTime);
method @androidx.compose.runtime.Composable @com.google.android.horologist.annotations.ExperimentalHorologistApi public static void MarqueeText(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional androidx.compose.ui.text.TextStyle style, optional int textAlign, optional float followGap, optional float startGap, optional float startEdgeGradientWidth, optional float endEdgeGradientWidth, optional float marqueeDpPerSecond, optional long pauseTime);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we think the new form with separate startGap, startEdgeGradientWidth, endEdgeGradientWidth is the right general form, then we should either

a) Remove the old form.
b) Leave them for compatibility, maybe deprecated.

As in the old methods are gone, since you have changed the params, added startGap.

method @androidx.compose.runtime.Composable @com.google.android.horologist.annotations.ExperimentalHorologistApi public static void MarqueeText(String text, optional androidx.compose.ui.Modifier modifier, optional long color, optional androidx.compose.ui.text.TextStyle style, optional int textAlign, optional float followGap, optional float startGap, optional float edgeGradientWidth, optional float marqueeDpPerSecond, optional long pauseTime);
}

public final class PlaceholderChipKt {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.MarqueeSpacing
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.runtime.Composable
Expand Down Expand Up @@ -75,7 +74,10 @@ import kotlin.time.Duration.Companion.seconds
* @param textAlign The alignment of the text within the lines of the paragraph.
* See [TextStyle.textAlign].
* @param followGap the width between end of each scrolling text and the start of the following one.
* @param edgeGradientWidth the width of the fade out zone on the edges, so text isn't cut off
* @param startGap the padding in [Dp] added before the [text].
* @param startEdgeGradientWidth the width of the fade out zone on the start edge, so text isn't cut
* off harshly.
* @param endEdgeGradientWidth the width of the fade out zone on the end edge, so text isn't cut off
* harshly.
* @param marqueeDpPerSecond the speed of scrolling in dp per second.
* @param pauseTime the duration before initially scrolling and each additional scroll.
Expand All @@ -90,12 +92,17 @@ public fun MarqueeText(
style: TextStyle = LocalTextStyle.current,
textAlign: TextAlign = TextAlign.Left,
followGap: Dp = 96.dp,
edgeGradientWidth: Dp = 16.dp,
startGap: Dp = 0.dp,
startEdgeGradientWidth: Dp = 16.dp,
endEdgeGradientWidth: Dp = 16.dp,
marqueeDpPerSecond: Dp = 64.dp,
pauseTime: Duration = 4.seconds,
) {
val controller = remember(text, style) { MarqueeController(edgeGradientWidth) }
controller.edgeGradientWidth = edgeGradientWidth
val controller = remember(text, style) {
MarqueeController(startEdgeGradientWidth, endEdgeGradientWidth, startGap)
}
controller.startEdgeGradientWidth = startEdgeGradientWidth
controller.endEdgeGradientWidth = endEdgeGradientWidth

Text(
text = text,
Expand Down Expand Up @@ -135,6 +142,118 @@ public fun MarqueeText(
* @param textAlign The alignment of the text within the lines of the paragraph.
* See [TextStyle.textAlign].
* @param followGap the width between end of each scrolling text and the start of the following one.
* @param startGap the padding in [Dp] added before the [text].
* @param startEdgeGradientWidth the width of the fade out zone on the start edge, so text isn't cut
* off harshly.
* @param endEdgeGradientWidth the width of the fade out zone on the end edge, so text isn't cut off
* harshly.
* @param marqueeDpPerSecond the speed of scrolling in dp per second.
* @param pauseTime the duration before initially scrolling and each additional scroll.
*/
@ExperimentalHorologistApi
@Composable
public fun MarqueeText(
text: String,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a lot of new forms of this method, if this is a more advanced form and we want to keep the existing simpler forms, should we only add the AnnotatedString version?

modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
style: TextStyle = LocalTextStyle.current,
textAlign: TextAlign = TextAlign.Left,
followGap: Dp = 96.dp,
startGap: Dp = 0.dp,
startEdgeGradientWidth: Dp = 16.dp,
endEdgeGradientWidth: Dp = 16.dp,
marqueeDpPerSecond: Dp = 64.dp,
pauseTime: Duration = 4.seconds,
) {
MarqueeText(
text = AnnotatedString(text),
inlineContent = emptyMap(),
modifier = modifier,
color = color,
style = style,
textAlign = textAlign,
followGap = followGap,
startGap = startGap,
startEdgeGradientWidth = startEdgeGradientWidth,
endEdgeGradientWidth = endEdgeGradientWidth,
marqueeDpPerSecond = marqueeDpPerSecond,
pauseTime = pauseTime,
)
}

/**
* Show a single line Marquee text, with a pause (initial and between cycles) and speed.
*
* Otherwise is mostly the same as the [Text] composable, without params that don't apply for
* marquee, such as maxLines.
*
* Only scrolls if required, and otherwise uses textAlign to show the content in a
* stationary position.
*
* @param text The text to be displayed, where [AnnotatedString] allows multiple styles to be used.
* @param modifier [Modifier] to apply to this layout node.
* @param inlineContent A map store composables that replaces certain ranges of the text. It's
* used to insert composables into text layout. Check [InlineTextContent] for more information.
* @param color [Color] to apply to the text. If [Color.Unspecified], and [style] has no color set,
* this will be [LocalContentColor].
* @param style Style configuration for the text such as color, font, line height etc.
* @param textAlign The alignment of the text within the lines of the paragraph.
* See [TextStyle.textAlign].
* @param followGap the width between end of each scrolling text and the start of the following one.
* @param startGap the padding in [Dp] added before the [text].
* @param edgeGradientWidth the width of the fade out zone on the edges, so text isn't cut off
* harshly.
* @param marqueeDpPerSecond the speed of scrolling in dp per second.
* @param pauseTime the duration before initially scrolling and each additional scroll.
*/
@ExperimentalHorologistApi
@Composable
public fun MarqueeText(
text: AnnotatedString,
modifier: Modifier = Modifier,
inlineContent: Map<String, InlineTextContent> = mapOf(),
color: Color = Color.Unspecified,
style: TextStyle = LocalTextStyle.current,
textAlign: TextAlign = TextAlign.Left,
followGap: Dp = 96.dp,
startGap: Dp = 0.dp,
edgeGradientWidth: Dp = 16.dp,
marqueeDpPerSecond: Dp = 64.dp,
pauseTime: Duration = 4.seconds,
) {
MarqueeText(
text = text,
inlineContent = inlineContent,
modifier = modifier,
color = color,
style = style,
textAlign = textAlign,
followGap = followGap,
startGap = startGap,
startEdgeGradientWidth = edgeGradientWidth,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also set endEdgeGradientWidth?

marqueeDpPerSecond = marqueeDpPerSecond,
pauseTime = pauseTime,
)
}

/**
* Show a single line Marquee text, with a pause (initial and between cycles) and speed.
*
* Otherwise is mostly the same as the [Text] composable, without params that don't apply for
* marquee, such as maxLines.
*
* Only scrolls if required, and otherwise uses textAlign to show the content in a
* stationary position.
*
* @param text The text to be displayed.
* @param modifier [Modifier] to apply to this layout node.
* @param color [Color] to apply to the text. If [Color.Unspecified], and [style] has no color set,
* this will be [LocalContentColor].
* @param style Style configuration for the text such as color, font, line height etc.
* @param textAlign The alignment of the text within the lines of the paragraph.
* See [TextStyle.textAlign].
* @param followGap the width between end of each scrolling text and the start of the following one.
* @param startGap the padding in [Dp] added before the [text].
* @param edgeGradientWidth the width of the fade out zone on the edges, so text isn't cut off
* harshly.
* @param marqueeDpPerSecond the speed of scrolling in dp per second.
Expand All @@ -149,6 +268,7 @@ public fun MarqueeText(
style: TextStyle = LocalTextStyle.current,
textAlign: TextAlign = TextAlign.Left,
followGap: Dp = 96.dp,
startGap: Dp = 0.dp,
edgeGradientWidth: Dp = 16.dp,
marqueeDpPerSecond: Dp = 64.dp,
pauseTime: Duration = 4.seconds,
Expand All @@ -161,15 +281,21 @@ public fun MarqueeText(
style = style,
textAlign = textAlign,
followGap = followGap,
startGap = startGap,
edgeGradientWidth = edgeGradientWidth,
marqueeDpPerSecond = marqueeDpPerSecond,
pauseTime = pauseTime,
)
}

private class MarqueeController(edgeGradientWidth: Dp) {
private class MarqueeController(
startEdgeGradientWidth: Dp,
endEdgeGradientWidth: Dp,
startGap: Dp,
) {

var edgeGradientWidth: Dp by mutableStateOf(edgeGradientWidth)
var startEdgeGradientWidth: Dp by mutableStateOf(startEdgeGradientWidth)
var endEdgeGradientWidth: Dp by mutableStateOf(endEdgeGradientWidth)
private var needsScrolling by mutableStateOf(false)
private var contentWidth: Int by mutableStateOf(-1)

Expand Down Expand Up @@ -197,18 +323,41 @@ private class MarqueeController(edgeGradientWidth: Dp) {

private val padding = object : PaddingValues {
override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp =
if (needsScrolling && layoutDirection == LayoutDirection.Ltr) edgeGradientWidth else 0.dp
if (layoutDirection == LayoutDirection.Ltr) {
if (startGap > 0.dp) {
startGap
} else if (needsScrolling) {
startEdgeGradientWidth
} else {
0.dp
}
} else {
0.dp
}

override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp =
if (needsScrolling && layoutDirection != LayoutDirection.Ltr) edgeGradientWidth else 0.dp
if (layoutDirection != LayoutDirection.Ltr) {
if (startGap > 0.dp) {
startGap
} else if (needsScrolling) {
startEdgeGradientWidth
} else {
0.dp
}
} else {
0.dp
}

override fun calculateTopPadding(): Dp = 0.dp
override fun calculateBottomPadding(): Dp = 0.dp
}
val insideMarqueeModifier: Modifier = Modifier.padding(padding)

private fun Modifier.drawFadeGradient() = this.drawWithCache {
val width = edgeGradientWidth.toPx()
val leftWidth =
if (layoutDirection == LayoutDirection.Ltr) startEdgeGradientWidth.toPx() else endEdgeGradientWidth.toPx()
val rightWidth =
if (layoutDirection == LayoutDirection.Ltr) endEdgeGradientWidth.toPx() else startEdgeGradientWidth.toPx()
// Create the brush here and leverage it within the onDrawWithContent block below
// The brush will only be instantiated on first render and if the size of the composable
// changes. Otherwise the same brush instance will be used.
Expand All @@ -218,22 +367,22 @@ private class MarqueeController(edgeGradientWidth: Dp) {
Color.Black,
),
startX = 0f,
endX = width,
endX = leftWidth,
)
val rightBrush = Brush.horizontalGradient(
listOf(
Color.Transparent,
Color.Black,
),
startX = size.width,
endX = size.width - width,
endX = size.width - rightWidth,
)
onDrawWithContent {
drawContent()

if (needsScrolling) {
drawRect(
size = Size(width, size.height),
size = Size(leftWidth, size.height),
topLeft = Offset(
0f,
0f,
Expand All @@ -242,9 +391,9 @@ private class MarqueeController(edgeGradientWidth: Dp) {
blendMode = BlendMode.DstIn,
)
drawRect(
size = Size(width, size.height),
size = Size(rightWidth, size.height),
topLeft = Offset(
size.width - width,
size.width - rightWidth,
0f,
),
brush = rightBrush,
Expand Down
1 change: 1 addition & 0 deletions media/sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ android {
unitTests {
isIncludeAndroidResources = true
}
animationsDisabled = true
}

buildTypes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.compose.runtime.getValue
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.res.stringResource
import com.google.android.horologist.audio.ui.components.actions.SettingsButton
Expand All @@ -37,12 +38,14 @@ import com.google.android.horologist.mediasample.R
@Composable
public fun FavoriteButton(
modifier: Modifier = Modifier,
iconAlignment: Alignment = Alignment.Center,
) {
var faved by remember { mutableStateOf(false) }
SettingsButton(
modifier = modifier,
onClick = { faved = !faved },
imageVector = if (faved) Icons.Default.Favorite else Icons.Default.FavoriteBorder,
contentDescription = stringResource(R.string.favorite_content_description),
iconAlignment = iconAlignment,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.wear.compose.material.MaterialTheme
import com.google.android.horologist.audio.ui.VolumeViewModel
import com.google.android.horologist.audio.ui.components.toAudioOutputUi
import com.google.android.horologist.images.base.paintable.DrawableResPaintable
import com.google.android.horologist.images.coil.CoilPaintable
import com.google.android.horologist.logo.R
import com.google.android.horologist.media.ui.components.PodcastControlButtons
import com.google.android.horologist.media.ui.components.animated.AnimatedMediaControlButtons
import com.google.android.horologist.media.ui.components.animated.AnimatedMediaInfoDisplay
Expand Down Expand Up @@ -75,6 +77,7 @@ fun UampMediaPlayerScreen(
AnimatedMediaInfoDisplay(
media = playerUiState.media,
loading = !playerUiState.connected || playerUiState.media is MediaUiModel.Loading,
appIcon = DrawableResPaintable(R.drawable.ic_horologist_monochrome),
)
} else {
DefaultMediaInfoDisplay(playerUiState)
Expand All @@ -85,7 +88,6 @@ fun UampMediaPlayerScreen(
volumeUiState = volumeUiState,
audioOutputUi = audioOutput.toAudioOutputUi(),
onVolumeClick = onVolumeClick,
enabled = state.connected && state.media != null,
)
},
controlButtons = { playerUiController, playerUiState ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@ package com.google.android.horologist.mediasample.ui.player

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.google.android.horologist.audio.ui.VolumeUiState
import com.google.android.horologist.audio.ui.components.AudioOutputUi
import com.google.android.horologist.audio.ui.components.SettingsButtonsDefaults
import com.google.android.horologist.audio.ui.components.actions.SetAudioOutputButton
import com.google.android.horologist.logo.R

/**
* Settings buttons for the UAMP media app.
Expand All @@ -37,24 +36,21 @@ public fun UampSettingsButtons(
audioOutputUi: AudioOutputUi,
onVolumeClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
) {
Row(
modifier = modifier,
modifier = modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly,
horizontalArrangement = Arrangement.Center,
) {
FavoriteButton()

SettingsButtonsDefaults.BrandIcon(
iconId = R.drawable.ic_stat_horologist,
enabled = enabled,
)

SetAudioOutputButton(
modifier = Modifier.weight(1f),
onVolumeClick = onVolumeClick,
volumeUiState = volumeUiState,
audioOutputUi = audioOutputUi,
)

FavoriteButton(
modifier = Modifier.weight(1f),
)
}
}
Loading