Skip to content

Commit

Permalink
Add small device support to Top Show EoY story (#3114)
Browse files Browse the repository at this point in the history
  • Loading branch information
MiSikora authored Oct 28, 2024
1 parent 531dabc commit a934d11
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
Expand All @@ -28,10 +29,11 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
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
Expand All @@ -48,6 +50,7 @@ import au.com.shiftyjelly.pocketcasts.models.to.TopPodcast
import dev.shreyaspatil.capturable.capturable
import java.io.File
import kotlin.math.sqrt
import au.com.shiftyjelly.pocketcasts.images.R as IR
import au.com.shiftyjelly.pocketcasts.ui.R as UR

@OptIn(ExperimentalComposeUiApi::class)
Expand All @@ -58,63 +61,101 @@ internal fun TopShowStory(
controller: StoryCaptureController,
onShareStory: (File) -> Unit,
) {
Box(
modifier = Modifier.capturable(controller.captureController(story)),
Box {
Column(
modifier = Modifier
.capturable(controller.captureController(story))
.fillMaxSize()
.background(story.backgroundColor)
.padding(top = measurements.closeButtonBottomEdge),
) {
TopShowCover(
story = story,
measurements = measurements,
controller = controller,
)
TopShowInfo(
story = story,
measurements = measurements,
controller = controller,
onShareStory = onShareStory,
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(measurements.closeButtonBottomEdge)
.background(story.backgroundColor),
)
}
}

@Composable
private fun ColumnScope.TopShowCover(
story: Story.TopShow,
measurements: EndOfYearMeasurements,
controller: StoryCaptureController,
) {
BoxWithConstraints(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.background(story.backgroundColor),
) {
val shapeSize = measurements.width * 1.12f
val coverSize = shapeSize * sqrt(2f)
val coverOffset = measurements.closeButtonBottomEdge
PodcastImage(
uuid = story.show.uuid,
elevation = 0.dp,
roundCorners = false,
modifier = Modifier
.requiredSize(coverSize)
.offset(y = coverOffset),
modifier = Modifier.requiredSize(maxOf(maxWidth, maxHeight)),
)

val transition = rememberInfiniteTransition(label = "transition")
val rotation = transition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(tween(40_000, easing = LinearEasing)),
label = "rotation",
)
val shapeSizePx = LocalDensity.current.run { shapeSize.toPx() }
val shapeOffsetPx = LocalDensity.current.run { (coverOffset * 0.6f + (coverSize - shapeSize) / 2).toPx() }
Box(
modifier = Modifier
.fillMaxSize()
.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
.drawWithCache {
val path = Path().apply {
val dentSize = (shapeSizePx - size.width) / 2

moveTo(-dentSize, shapeOffsetPx)
lineTo(size.width / 2, (shapeOffsetPx) + dentSize)
lineTo(size.width + dentSize, shapeOffsetPx)
lineTo(size.width, shapeOffsetPx + shapeSizePx / 2)
lineTo(size.width + dentSize, shapeOffsetPx + shapeSizePx)
lineTo(size.width / 2, shapeOffsetPx + shapeSizePx - dentSize)
lineTo(-dentSize, shapeOffsetPx + shapeSizePx)
lineTo(0f, shapeOffsetPx + shapeSizePx / 2)
lineTo(-dentSize, shapeOffsetPx)
val widthPx = density.run { maxWidth.toPx() }
val heightPx = density.run { maxHeight.toPx() }
val edgeSize = heightPx / sqrt(2f)
val dentSize = edgeSize / 20

val path = Path().apply {
moveTo(0f, 0f)
lineTo(edgeSize / 2, dentSize)
lineTo(edgeSize, 0f)
lineTo(edgeSize - dentSize, edgeSize / 2)
lineTo(edgeSize, edgeSize)
lineTo(edgeSize / 2, edgeSize - dentSize)
lineTo(0f, edgeSize)
lineTo(dentSize, edgeSize / 2)
lineTo(0f, 0f)
close()
}

onDrawWithContent {
drawContent()

rotate(
rotation.value,
pivot = Offset(x = size.width / 2, y = shapeOffsetPx + shapeSizePx / 2),
degrees = if (controller.isSharing) 0f else rotation.value,
pivot = Offset(widthPx / 2, heightPx / 2),
) {
drawPath(
color = Color(0xFFFFFFFF),
blendMode = BlendMode.DstOut,
path = path,
)
translate(
left = (widthPx - edgeSize) / 2,
top = (heightPx - edgeSize) / 2,
) {
drawPath(
color = Color(0xFFFFFFFF),
blendMode = BlendMode.DstOut,
path = path,
)
}
}
}
},
Expand All @@ -125,65 +166,61 @@ internal fun TopShowStory(
.background(story.backgroundColor),
)
}

Column(
modifier = Modifier.align(Alignment.BottomCenter),
) {
TextH10(
text = stringResource(
R.string.end_of_year_story_top_podcast_title,
story.show.title,
),
fontScale = measurements.smallDeviceFactor,
disableAutoScale = true,
color = colorResource(UR.color.coolgrey_90),
modifier = Modifier.padding(horizontal = 24.dp),
)
Spacer(
modifier = Modifier.height(16.dp),
)
TextP40(
text = stringResource(
R.string.end_of_year_story_top_podcast_subtitle,
story.show.playedEpisodeCount,
StatsHelper.secondsToFriendlyString(
story.show.playbackTime.inWholeSeconds,
LocalContext.current.resources,
),
),
fontSize = 15.sp,
disableAutoScale = true,
color = colorResource(UR.color.coolgrey_90),
modifier = Modifier.padding(horizontal = 24.dp),
)
ShareStoryButton(
story = story,
controller = controller,
onShare = onShareStory,
)
}

// Clip the rotating shape at top
Box(
Image(
painter = painterResource(IR.drawable.end_of_year_2024_sticker_8),
contentDescription = null,
modifier = Modifier
.align(Alignment.TopCenter)
.fillMaxWidth()
.height(measurements.closeButtonBottomEdge)
.background(story.backgroundColor),
.align(Alignment.TopStart)
.padding(start = 20.dp, top = 8.dp)
.size(
width = 215.dp * measurements.scale,
height = 103.dp * measurements.scale,
),
)
}
}

// Fake sticker: lH66LwxxgG8btQ8NrM0ldx-fi-3070_23365#986383416
val stickerWidth = 208.dp * measurements.scale
val stickerHeight = 87.dp * measurements.scale
Box(
modifier = Modifier
.align(Alignment.TopCenter)
.offset(
x = -stickerWidth / 3,
y = measurements.closeButtonBottomEdge + stickerHeight / 2,
)
.size(stickerWidth, stickerHeight)
.background(Color.Black, shape = CircleShape),
@Composable
private fun TopShowInfo(
story: Story.TopShow,
measurements: EndOfYearMeasurements,
controller: StoryCaptureController,
onShareStory: (File) -> Unit,
) {
Column(
modifier = Modifier.background(story.backgroundColor),
) {
TextH10(
text = stringResource(
R.string.end_of_year_story_top_podcast_title,
story.show.title,
),
fontScale = measurements.smallDeviceFactor,
disableAutoScale = true,
color = colorResource(UR.color.coolgrey_90),
modifier = Modifier.padding(horizontal = 24.dp),
)
Spacer(
modifier = Modifier.height(16.dp),
)
TextP40(
text = stringResource(
R.string.end_of_year_story_top_podcast_subtitle,
story.show.playedEpisodeCount,
StatsHelper.secondsToFriendlyString(
story.show.playbackTime.inWholeSeconds,
LocalContext.current.resources,
),
),
fontSize = 15.sp,
disableAutoScale = true,
color = colorResource(UR.color.coolgrey_90),
modifier = Modifier.padding(horizontal = 24.dp),
)
ShareStoryButton(
story = story,
controller = controller,
onShare = onShareStory,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="215dp"
android:height="103dp"
android:viewportWidth="215"
android:viewportHeight="103">
<path
android:fillColor="#000000"
android:pathData="M209.59,23.34C209.17,17.31 206.39,11.7 201.86,7.73C197.34,3.77 191.44,1.78 185.47,2.2L22.72,13.71C16.75,14.13 11.19,16.93 7.27,21.49C3.35,26.05 1.38,32 1.81,38.03C2.12,42.41 3.68,46.61 6.3,50.12C8.92,53.63 12.49,56.31 16.57,57.82C12.74,59.9 9.58,63.05 7.48,66.89C5.39,70.74 4.43,75.11 4.74,79.5C5.17,85.52 7.95,91.14 12.47,95.1C17,99.07 22.89,101.06 28.87,100.63L191.62,89.13C197.59,88.7 203.15,85.9 207.07,81.34C210.99,76.78 212.95,70.83 212.53,64.81C212.22,60.42 210.66,56.22 208.04,52.71C205.42,49.2 201.85,46.53 197.76,45.01C201.6,42.94 204.75,39.79 206.85,35.94C208.95,32.1 209.9,27.72 209.59,23.34Z" />
<path
android:fillColor="#F3CCF7"
android:pathData="M56.69,30.65L57.11,36.64L53.78,36.87L56.65,77.43L50,77.9L47.14,37.34L43.81,37.58L43.39,31.59L56.69,30.65ZM74.35,36.35L76.67,69.06C77,73.72 74.13,76.59 69.87,76.89C65.62,77.19 62.37,74.75 62.04,70.1L59.73,37.39C59.4,32.8 62.4,29.85 66.59,29.55C70.78,29.26 74.03,31.7 74.35,36.35ZM67.68,36.49C67.64,35.83 67.41,35.51 66.95,35.54C66.48,35.57 66.31,35.92 66.35,36.58L68.71,69.96C68.76,70.62 68.98,70.94 69.45,70.91C69.91,70.88 70.09,70.53 70.04,69.87L67.68,36.49ZM91.87,34.85L92.85,48.81C93.18,53.46 90.93,55.63 86.01,55.97L85.35,56.02L86.71,75.3L80.06,75.77L76.77,29.23L84.15,28.71C89,28.37 91.54,30.19 91.87,34.85ZM85.24,35.65C85.19,34.98 84.97,34.67 84.51,34.7L83.84,34.75L84.92,50.04L85.59,49.99C86.05,49.96 86.23,49.61 86.18,48.95L85.24,35.65ZM118.61,58.68L119.13,66.06C119.46,70.72 116.59,73.59 112.33,73.89C108.14,74.19 104.83,71.75 104.5,67.1L103.83,57.65L110.48,57.18L111.17,66.96C111.22,67.62 111.44,67.94 111.91,67.91C112.37,67.88 112.55,67.53 112.5,66.86L111.96,59.15C111.76,56.36 109.83,53.69 107.83,50.82C105.46,47.52 102.86,43.96 102.56,39.7L102.19,34.38C101.86,29.8 104.86,26.85 109.05,26.55C113.24,26.25 116.49,28.7 116.82,33.35L117.34,40.73L110.69,41.2L110.14,33.49C110.1,32.82 109.87,32.51 109.41,32.54C108.94,32.57 108.77,32.92 108.81,33.58L109.21,39.23C109.41,42.03 111.34,44.7 113.41,47.56C115.78,50.86 118.31,54.43 118.61,58.68ZM134.2,31.85L135.19,45.82C135.52,50.47 133.26,52.63 128.34,52.98L127.68,53.03L129.04,72.31L122.39,72.78L119.1,26.24L126.48,25.72C131.34,25.38 133.87,27.2 134.2,31.85ZM127.57,32.66C127.53,31.99 127.31,31.67 126.84,31.71L126.18,31.75L127.26,47.04L127.92,47C128.39,46.97 128.56,46.62 128.51,45.95L127.57,32.66ZM151.75,30.88L154.06,63.59C154.39,68.25 151.52,71.12 147.27,71.42C143.01,71.72 139.76,69.28 139.43,64.63L137.12,31.91C136.8,27.33 139.8,24.38 143.99,24.08C148.17,23.78 151.42,26.23 151.75,30.88ZM145.08,31.02C145.03,30.35 144.81,30.04 144.34,30.07C143.88,30.1 143.7,30.45 143.75,31.11L146.11,64.49C146.15,65.15 146.38,65.47 146.84,65.44C147.31,65.41 147.48,65.06 147.44,64.39L145.08,31.02ZM167.13,22.84L167.55,28.83L164.23,29.06L167.1,69.62L160.45,70.09L157.58,29.53L154.26,29.77L153.83,23.78L167.13,22.84Z" />
<path
android:fillColor="#F3CCF7"
android:pathData="M191.25,46.41C186.16,48.14 184.74,49.82 183.86,55.12C183.84,55.23 183.78,55.34 183.7,55.42C183.61,55.5 183.5,55.54 183.38,55.55C183.27,55.56 183.15,55.53 183.05,55.47C182.95,55.41 182.88,55.31 182.84,55.2C181.11,50.12 179.43,48.69 174.13,47.81C174.02,47.79 173.91,47.73 173.83,47.65C173.75,47.56 173.71,47.45 173.7,47.33C173.69,47.22 173.71,47.1 173.78,47C173.84,46.9 173.94,46.83 174.05,46.79C179.13,45.06 180.56,43.38 181.44,38.08C181.46,37.97 181.52,37.86 181.61,37.78C181.69,37.71 181.8,37.66 181.92,37.65C182.04,37.64 182.15,37.67 182.25,37.73C182.35,37.8 182.43,37.89 182.46,38C184.19,43.08 185.87,44.51 191.16,45.39C191.28,45.41 191.38,45.47 191.46,45.56C191.54,45.64 191.59,45.75 191.6,45.87C191.61,45.99 191.58,46.1 191.52,46.2C191.45,46.3 191.36,46.37 191.25,46.41Z" />
<path
android:fillColor="#F3CCF7"
android:pathData="M39.21,56.56C34.12,58.28 32.7,59.97 31.82,65.26C31.8,65.38 31.74,65.48 31.66,65.56C31.57,65.64 31.46,65.69 31.34,65.7C31.22,65.71 31.11,65.68 31.01,65.61C30.91,65.55 30.84,65.46 30.8,65.35C29.07,60.26 27.39,58.83 22.09,57.96C21.97,57.94 21.87,57.88 21.79,57.79C21.71,57.7 21.66,57.59 21.65,57.48C21.64,57.36 21.67,57.24 21.74,57.15C21.8,57.05 21.9,56.97 22.01,56.94C27.09,55.21 28.52,53.53 29.4,48.23C29.42,48.11 29.48,48.01 29.57,47.93C29.65,47.85 29.76,47.8 29.88,47.79C30,47.78 30.11,47.81 30.21,47.88C30.31,47.94 30.38,48.04 30.42,48.15C32.15,53.23 33.82,54.66 39.12,55.54C39.24,55.56 39.34,55.62 39.42,55.7C39.5,55.79 39.55,55.9 39.56,56.02C39.57,56.13 39.54,56.25 39.47,56.35C39.41,56.44 39.32,56.52 39.21,56.56Z" />
</vector>

0 comments on commit a934d11

Please sign in to comment.