Skip to content

Commit

Permalink
Merge pull request Automattic#428 from Automattic/task/410-stories-st…
Browse files Browse the repository at this point in the history
…ructure

End of Year: Add stories structure
  • Loading branch information
ashiagr committed Oct 21, 2022
2 parents ff8ce0c + 38559e4 commit f4114e8
Show file tree
Hide file tree
Showing 11 changed files with 347 additions and 21 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ dependencies {
implementation project(':modules:features:filters')
implementation project(':modules:features:navigation')
implementation project(':modules:features:account')
implementation project(':modules:features:endofyear')
}

task appStart(type: Exec, dependsOn: 'installDebug') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import au.com.shiftyjelly.pocketcasts.compose.bottomsheet.BottomSheetContentStat
import au.com.shiftyjelly.pocketcasts.compose.bottomsheet.ModalBottomSheet
import au.com.shiftyjelly.pocketcasts.databinding.ActivityMainBinding
import au.com.shiftyjelly.pocketcasts.discover.view.DiscoverFragment
import au.com.shiftyjelly.pocketcasts.endofyear.StoriesFragment
import au.com.shiftyjelly.pocketcasts.filters.FiltersFragment
import au.com.shiftyjelly.pocketcasts.localization.helper.LocaliseHelper
import au.com.shiftyjelly.pocketcasts.models.entity.Episode
Expand Down Expand Up @@ -465,7 +466,10 @@ class MainActivity :
summaryText = stringResource(LR.string.end_of_year_launch_modal_summary),
primaryButton = Button.Primary(
label = stringResource(LR.string.end_of_year_launch_modal_primary_button_title),
onClick = {}
onClick = {
StoriesFragment.newInstance()
.show(supportFragmentManager, "stories_dialog")
}
),
secondaryButton = Button.Secondary(
label = stringResource(LR.string.end_of_year_launch_modal_secondary_button_title),
Expand Down
18 changes: 18 additions & 0 deletions modules/features/endofyear/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apply from: "../../modules.gradle"

android {
namespace 'au.com.shiftyjelly.pocketcasts.endofyear'
buildFeatures {
viewBinding true
dataBinding = true
compose true
}
}

dependencies {
// services
implementation project(':modules:services:compose')
implementation project(':modules:services:localization')
implementation project(':modules:services:ui')
implementation project(':modules:services:views')
}
4 changes: 4 additions & 0 deletions modules/features/endofyear/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http:https://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package au.com.shiftyjelly.pocketcasts.endofyear

import androidx.annotation.FloatRange
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.progressSemantics
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.dp

private val StrokeWidth = 2.dp
private val GapWidth = 8.dp
private val SegmentHeight = StrokeWidth
private const val IndicatorBackgroundOpacity = 0.24f

@Composable
fun SegmentedProgressIndicator(
@FloatRange(from = 0.0, to = 1.0) progress: Float,
modifier: Modifier = Modifier,
color: Color = Color.White,
backgroundColor: Color = color.copy(alpha = IndicatorBackgroundOpacity),
numberOfSegments: Int,
) {
Canvas(
modifier
.progressSemantics(progress)
.fillMaxWidth()
.height(SegmentHeight)
.focusable()
) {
drawSegmentsBackground(backgroundColor, numberOfSegments)
drawSegments(progress, color, numberOfSegments)
}
}

private fun DrawScope.drawSegmentsBackground(
color: Color,
numberOfSegments: Int,
) = drawSegments(1f, color, numberOfSegments)

private fun DrawScope.drawSegments(
endFraction: Float,
color: Color,
numberOfSegments: Int,
) {
val width = size.width
val height = size.height
// Start drawing from the vertical center of the stroke
val yOffset = height / 2

val barEnd = endFraction * width

val segmentWidth = calculateSegmentWidth(numberOfSegments)
val segmentAndGapWidth = segmentWidth + GapWidth.toPx()

repeat(numberOfSegments) { index ->
val xOffsetStart = index * segmentAndGapWidth
val shouldDrawLine = xOffsetStart < barEnd
if (shouldDrawLine) {
val xOffsetEnd = (xOffsetStart + segmentWidth).coerceAtMost(barEnd)
// Progress line
drawLine(color, Offset(xOffsetStart, yOffset), Offset(xOffsetEnd, yOffset), StrokeWidth.toPx())
}
}
}

private fun DrawScope.calculateSegmentWidth(
numberOfSegments: Int,
): Float {
val width = size.width
val gapsWidth = (numberOfSegments - 1) * GapWidth.toPx()
return (width - gapsWidth) / numberOfSegments
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package au.com.shiftyjelly.pocketcasts.endofyear

import android.graphics.Color
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import au.com.shiftyjelly.pocketcasts.compose.AppTheme
import au.com.shiftyjelly.pocketcasts.ui.R
import au.com.shiftyjelly.pocketcasts.ui.helper.StatusBarColor
import au.com.shiftyjelly.pocketcasts.views.fragments.BaseDialogFragment

class StoriesFragment : BaseDialogFragment() {
override val statusBarColor: StatusBarColor
get() = StatusBarColor.Custom(Color.BLACK, true)

override fun onCreate(savedInstance: Bundle?) {
super.onCreate(savedInstance)
setStyle(STYLE_NORMAL, R.style.BottomSheetDialogThemeBlack)
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
return ComposeView(requireContext()).apply {
setContent {
AppTheme(theme.activeTheme) {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
StoriesScreen(
onCloseClicked = { dismiss() },
)
}
}
}
}

companion object {
fun newInstance() = StoriesFragment()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package au.com.shiftyjelly.pocketcasts.endofyear

import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
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.shape.RoundedCornerShape
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Share
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import au.com.shiftyjelly.pocketcasts.compose.AppTheme
import au.com.shiftyjelly.pocketcasts.compose.bars.NavigationButton
import au.com.shiftyjelly.pocketcasts.compose.buttons.RowOutlinedButton
import au.com.shiftyjelly.pocketcasts.compose.preview.ThemePreviewParameterProvider
import au.com.shiftyjelly.pocketcasts.ui.theme.Theme
import au.com.shiftyjelly.pocketcasts.localization.R as LR

private const val ProgressDurationMs = 5_000
private val ShareButtonStrokeWidth = 2.dp
private val StoryViewCornerSize = 10.dp
private const val NumberOfSegments = 2

@Composable
fun StoriesScreen(
onCloseClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
var running by remember { mutableStateOf(false) }
val progress: Float by animateFloatAsState(
if (running) 1f else 0f,
animationSpec = tween(
durationMillis = ProgressDurationMs,
easing = LinearEasing
)
)
Box(modifier = modifier.background(color = Color.Black)) {
StoryView(color = Color.Gray)
SegmentedProgressIndicator(
progress = progress,
numberOfSegments = NumberOfSegments,
modifier = modifier
.padding(8.dp)
.fillMaxWidth(),
)
CloseButtonView(onCloseClicked)
}

LaunchedEffect(Unit) {
running = true
}
}

@Composable
private fun StoryView(
color: Color,
modifier: Modifier = Modifier,
) {
Column {
Box(
modifier = modifier
.fillMaxSize()
.weight(weight = 1f, fill = true)
.clip(RoundedCornerShape(StoryViewCornerSize))
.background(color = color)
) {}
ShareButton()
}
}

@Composable
private fun ShareButton() {
RowOutlinedButton(
text = stringResource(id = LR.string.share),
border = BorderStroke(ShareButtonStrokeWidth, Color.White),
colors = ButtonDefaults
.outlinedButtonColors(
backgroundColor = Color.Transparent,
contentColor = Color.White,
),
iconImage = Icons.Default.Share,
onClick = {}
)
}

@Composable
private fun CloseButtonView(
onCloseClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(top = 16.dp),
horizontalArrangement = Arrangement.End
) {
IconButton(
onClick = onCloseClicked
) {
Icon(
imageVector = NavigationButton.Close.image,
contentDescription = stringResource(NavigationButton.Close.contentDescription),
tint = Color.White
)
}
}
}

@Preview(showBackground = true)
@Composable
private fun StoriesScreenPreview(
@PreviewParameter(ThemePreviewParameterProvider::class) themeType: Theme.ThemeType,
) {
AppTheme(themeType) {
StoriesScreen(
onCloseClicked = {}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ fun BottomSheetContent(

Spacer(modifier = modifier.height(16.dp))

Button(onClick = content.primaryButton.onClick) {
Button(
onClick = {
onDismiss.invoke()
content.primaryButton.onClick.invoke()
}
) {
Text(text = content.primaryButton.label)
}

Expand Down
Loading

0 comments on commit f4114e8

Please sign in to comment.