diff --git a/app/src/main/java/au/com/shiftyjelly/pocketcasts/ui/MainActivity.kt b/app/src/main/java/au/com/shiftyjelly/pocketcasts/ui/MainActivity.kt index fd76bcad354..ad20a730898 100644 --- a/app/src/main/java/au/com/shiftyjelly/pocketcasts/ui/MainActivity.kt +++ b/app/src/main/java/au/com/shiftyjelly/pocketcasts/ui/MainActivity.kt @@ -38,7 +38,8 @@ import au.com.shiftyjelly.pocketcasts.compose.AppTheme import au.com.shiftyjelly.pocketcasts.databinding.ActivityMainBinding import au.com.shiftyjelly.pocketcasts.discover.view.DiscoverFragment import au.com.shiftyjelly.pocketcasts.endofyear.StoriesDataSource -import au.com.shiftyjelly.pocketcasts.endofyear.StoriesFragment +import au.com.shiftyjelly.pocketcasts.endofyear.StoriesPage +import au.com.shiftyjelly.pocketcasts.endofyear.StoriesViewModel import au.com.shiftyjelly.pocketcasts.endofyear.views.EndOfYearLaunchBottomSheet import au.com.shiftyjelly.pocketcasts.filters.FiltersFragment import au.com.shiftyjelly.pocketcasts.localization.helper.LocaliseHelper @@ -167,6 +168,7 @@ class MainActivity : private lateinit var observeUpNext: LiveData private val viewModel: MainActivityViewModel by viewModels() + private val storiesViewModel: StoriesViewModel by viewModels() private val disposables = CompositeDisposable() private var videoPlayerShown: Boolean = false private var overrideNextRefreshTimer: Boolean = false @@ -496,11 +498,19 @@ class MainActivity : private fun setupEndOfYearLaunchBottomSheet() { binding.modalBottomSheet.setContent { - AppTheme(themeType = theme.activeTheme) { + var showDialog by rememberSaveable { mutableStateOf(false) } + if (showDialog) { + StoriesPage( + viewModel = storiesViewModel, + showDialog = showDialog, + theme = theme, + onCloseClicked = { showDialog = false }, + ) + } + AppTheme(theme.activeTheme) { EndOfYearLaunchBottomSheet( onClick = { - StoriesFragment.newInstance() - .show(supportFragmentManager, "stories_dialog") + showDialog = true } ) } diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoriesFragment.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoriesFragment.kt deleted file mode 100644 index 66ef0754d84..00000000000 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoriesFragment.kt +++ /dev/null @@ -1,82 +0,0 @@ -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.activity.result.contract.ActivityResultContracts -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.core.app.ShareCompat -import androidx.fragment.app.viewModels -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.utils.FileUtil -import au.com.shiftyjelly.pocketcasts.views.fragments.BaseDialogFragment -import timber.log.Timber -import java.io.File -import au.com.shiftyjelly.pocketcasts.localization.R as LR - -class StoriesFragment : BaseDialogFragment() { - private val viewModel: StoriesViewModel by viewModels() - override val statusBarColor: StatusBarColor - get() = StatusBarColor.Custom(Color.BLACK, true) - - private val shareLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - /* Share activity dismissed, start paused story */ - viewModel.start() - } - - 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) - StoriesPage( - viewModel = viewModel, - onCloseClicked = { dismiss() }, - onShareClicked = { onCaptureBitmap -> - viewModel.onShareClicked( - onCaptureBitmap, - requireContext(), - ::showShareForFile - ) - } - ) - } - } - } - } - - private fun showShareForFile(file: File) { - val context = requireContext() - try { - val uri = FileUtil.getUriForFile(context, file) - - val chooserIntent = ShareCompat.IntentBuilder(context) - .setType("image/png") - .addStream(uri) - .setChooserTitle(LR.string.end_of_year_share_via) - .createChooserIntent() - - shareLauncher.launch(chooserIntent) - } catch (e: Exception) { - Timber.e(e) - } - } - - companion object { - fun newInstance() = StoriesFragment() - } -} diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoriesPage.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoriesPage.kt index cd2abaf3b9a..a1b479fc05b 100644 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoriesPage.kt +++ b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/StoriesPage.kt @@ -1,6 +1,14 @@ package au.com.shiftyjelly.pocketcasts.endofyear +import android.app.Activity +import android.content.Context +import android.content.ContextWrapper +import android.content.Intent +import android.content.pm.ActivityInfo import android.graphics.Bitmap +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.FloatRange import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background @@ -12,6 +20,7 @@ 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.shape.RoundedCornerShape import androidx.compose.material.ButtonDefaults import androidx.compose.material.CircularProgressIndicator @@ -20,21 +29,28 @@ 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.DisposableEffect import androidx.compose.runtime.collectAsState 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.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalContext 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.DpSize import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import androidx.core.app.ShareCompat import au.com.shiftyjelly.pocketcasts.compose.AppTheme import au.com.shiftyjelly.pocketcasts.compose.bars.NavigationButton import au.com.shiftyjelly.pocketcasts.compose.buttons.RowOutlinedButton @@ -62,14 +78,62 @@ import au.com.shiftyjelly.pocketcasts.endofyear.views.stories.StoryTopFivePodcas import au.com.shiftyjelly.pocketcasts.endofyear.views.stories.StoryTopListenedCategoriesView import au.com.shiftyjelly.pocketcasts.endofyear.views.stories.StoryTopPodcastView import au.com.shiftyjelly.pocketcasts.models.db.helper.ListenedNumbers +import au.com.shiftyjelly.pocketcasts.ui.helper.FragmentHostListener +import au.com.shiftyjelly.pocketcasts.ui.helper.StatusBarColor import au.com.shiftyjelly.pocketcasts.ui.theme.Theme +import au.com.shiftyjelly.pocketcasts.utils.FileUtil +import au.com.shiftyjelly.pocketcasts.utils.Util +import timber.log.Timber +import java.io.File import au.com.shiftyjelly.pocketcasts.localization.R as LR private val ShareButtonStrokeWidth = 2.dp private val StoryViewCornerSize = 10.dp +private val StoriesViewMaxSize = 700.dp +private const val MaxHeightPercentFactor = 0.9f +const val StoriesViewAspectRatioForTablet = 2f +@OptIn(ExperimentalComposeUiApi::class) @Composable fun StoriesPage( + viewModel: StoriesViewModel, + showDialog: Boolean, + onCloseClicked: () -> Unit, + theme: Theme, + modifier: Modifier = Modifier +) { + if (showDialog) { + val shareLauncher = rememberLauncherForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { + /* Share activity dismissed, start paused story */ + viewModel.start() + } + + val context = LocalContext.current + + val isTablet = Util.isTablet(context) + if (!isTablet) LockScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + UpdateSystemBarColors(theme) + + val dialogSize = remember { getDialogSize(context) } + Dialog( + onDismissRequest = { onCloseClicked.invoke() }, + properties = DialogProperties(usePlatformDefaultWidth = isTablet), + ) { + Box(modifier = modifier.size(dialogSize)) { + DialogContent(viewModel, onCloseClicked) { + viewModel.onShareClicked(it, context) { file -> + showShareForFile(context, file, shareLauncher) + } + } + } + } + } +} + +@Composable +fun DialogContent( viewModel: StoriesViewModel, onCloseClicked: () -> Unit, onShareClicked: (() -> Bitmap) -> Unit, @@ -111,21 +175,18 @@ private fun StoriesView( var onCaptureBitmap: (() -> Bitmap)? = null state.currentStory?.let { story -> Box(modifier = modifier.weight(weight = 1f, fill = true)) { - if (!story.isInteractive) { - onCaptureBitmap = - convertibleToBitmap(content = { StorySharableContent(story, onReplayClicked, modifier) }) - } - StorySwitcher( - onSkipPrevious = onSkipPrevious, - onSkipNext = onSkipNext, - onPause = onPause, - onStart = onStart, - ) { - if (story.isInteractive) { - onCaptureBitmap = - convertibleToBitmap(content = { StorySharableContent(story, onReplayClicked, modifier) }) - } - } + onCaptureBitmap = + convertibleToBitmap(content = { + StorySharableContent( + story, + onSkipPrevious, + onSkipNext, + onPause, + onStart, + onReplayClicked, + modifier + ) + }) SegmentedProgressIndicator( progress = progress, segmentsData = state.segmentsData, @@ -147,26 +208,37 @@ private fun StoriesView( @Composable private fun StorySharableContent( story: Story, + onSkipPrevious: () -> Unit, + onSkipNext: () -> Unit, + onPause: () -> Unit, + onStart: () -> Unit, onReplayClicked: () -> Unit, modifier: Modifier, ) { - Box( - modifier = modifier - .fillMaxSize() - .clip(RoundedCornerShape(StoryViewCornerSize)) - .background(color = story.backgroundColor), - contentAlignment = Alignment.Center + StorySwitcher( + onSkipPrevious = onSkipPrevious, + onSkipNext = onSkipNext, + onPause = onPause, + onStart = onStart, ) { - when (story) { - is StoryIntro -> StoryIntroView(story) - is StoryListeningTime -> StoryListeningTimeView(story) - is StoryListenedCategories -> StoryListenedCategoriesView(story) - is StoryTopListenedCategories -> StoryTopListenedCategoriesView(story) - is StoryListenedNumbers -> StoryListenedNumbersView(story) - is StoryTopPodcast -> StoryTopPodcastView(story) - is StoryTopFivePodcasts -> StoryTopFivePodcastsView(story) - is StoryLongestEpisode -> StoryLongestEpisodeView(story) - is StoryEpilogue -> StoryEpilogueView(story, onReplayClicked) + Box( + modifier = modifier + .fillMaxSize() + .clip(RoundedCornerShape(StoryViewCornerSize)) + .background(color = story.backgroundColor), + contentAlignment = Alignment.Center + ) { + when (story) { + is StoryIntro -> StoryIntroView(story) + is StoryListeningTime -> StoryListeningTimeView(story) + is StoryListenedCategories -> StoryListenedCategoriesView(story) + is StoryTopListenedCategories -> StoryTopListenedCategoriesView(story) + is StoryListenedNumbers -> StoryListenedNumbersView(story) + is StoryTopPodcast -> StoryTopPodcastView(story) + is StoryTopFivePodcasts -> StoryTopFivePodcastsView(story) + is StoryLongestEpisode -> StoryLongestEpisodeView(story) + is StoryEpilogue -> StoryEpilogueView(story, onReplayClicked) + } } } } @@ -309,6 +381,75 @@ private fun StorySwitcher( } } +private fun showShareForFile( + context: Context, + file: File, + shareLauncher: ActivityResultLauncher, +) { + try { + val uri = FileUtil.getUriForFile(context, file) + + val chooserIntent = ShareCompat.IntentBuilder(context) + .setType("image/png") + .addStream(uri) + .setChooserTitle(LR.string.end_of_year_share_via) + .createChooserIntent() + + shareLauncher.launch(chooserIntent) + } catch (e: Exception) { + Timber.e(e) + } +} + +private fun getDialogSize(context: Context): DpSize { + val configuration = context.resources.configuration + val screenHeightInDp = configuration.screenHeightDp + val screenWidthInDp = configuration.screenWidthDp + + var dialogHeight = screenHeightInDp.toFloat() + var dialogWidth = screenWidthInDp.toFloat() + if (Util.isTablet(context)) { + dialogHeight = (screenHeightInDp * MaxHeightPercentFactor).coerceAtMost(StoriesViewMaxSize.value) + dialogWidth = dialogHeight / StoriesViewAspectRatioForTablet + } + + return DpSize(dialogWidth.dp, dialogHeight.dp) +} + +@Composable +fun LockScreenOrientation(orientation: Int) { + val context = LocalContext.current + DisposableEffect(Unit) { + val activity = context.findActivity() ?: return@DisposableEffect onDispose {} + val originalOrientation = activity.requestedOrientation + activity.requestedOrientation = orientation + onDispose { + // restore original orientation when view disappears + activity.requestedOrientation = originalOrientation + } + } +} + +@Composable +fun UpdateSystemBarColors(theme: Theme) { + val context = LocalContext.current + DisposableEffect(Unit) { + val activity = context.findActivity() ?: return@DisposableEffect onDispose {} + theme.updateWindowStatusBar(activity.window, StatusBarColor.Custom(android.graphics.Color.BLACK, true), activity) + theme.setNavigationBarColor(activity.window, true, android.graphics.Color.BLACK) + onDispose { + // restore original system colors + (activity as FragmentHostListener).updateSystemColors() + } + } +} + +fun Context.findActivity(): Activity? = when (this) { + is Activity -> this + is ContextWrapper -> baseContext.findActivity() + else -> null +} + @Preview(showBackground = true) @Composable private fun StoriesScreenPreview( diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/Story.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/Story.kt index 3584ed72463..d4952d1caa3 100644 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/Story.kt +++ b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/Story.kt @@ -5,7 +5,6 @@ import au.com.shiftyjelly.pocketcasts.utils.seconds abstract class Story { open val storyLength: Long = 2.seconds() - open val backgroundColor: Color = Color.Transparent + open val backgroundColor: Color = Color.Black val tintColor: Color = Color.White - open val isInteractive: Boolean = false } diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/StoryEpilogue.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/StoryEpilogue.kt index de34cb60da7..a0e881e51b6 100644 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/StoryEpilogue.kt +++ b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/StoryEpilogue.kt @@ -1,5 +1,3 @@ package au.com.shiftyjelly.pocketcasts.endofyear.stories -class StoryEpilogue : Story() { - override val isInteractive: Boolean = true -} +class StoryEpilogue : Story() diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/StoryIntro.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/StoryIntro.kt index cd2ddad6715..d23022732b3 100644 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/StoryIntro.kt +++ b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/stories/StoryIntro.kt @@ -1,3 +1,6 @@ package au.com.shiftyjelly.pocketcasts.endofyear.stories -class StoryIntro : Story() +import androidx.compose.ui.graphics.Color +class StoryIntro : Story() { + override val backgroundColor: Color = Color(0xFF1A1A1A) +} diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/SnapShot.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/SnapShot.kt index 134a1626bed..46a48c31cfb 100644 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/SnapShot.kt +++ b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/SnapShot.kt @@ -1,12 +1,19 @@ package au.com.shiftyjelly.pocketcasts.endofyear.views +import android.content.Context import android.graphics.Bitmap +import android.graphics.Canvas +import android.view.View +import android.view.WindowManager +import androidx.appcompat.widget.LinearLayoutCompat import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.viewinterop.AndroidView -import androidx.core.view.drawToBitmap +import au.com.shiftyjelly.pocketcasts.endofyear.StoriesViewAspectRatioForTablet +import au.com.shiftyjelly.pocketcasts.utils.Util +import au.com.shiftyjelly.pocketcasts.utils.extensions.deviceAspectRatio /* Returns a callback to get bitmap for the passed composable. The composable is converted to ComposeView and laid out into AndroidView otherwise an illegal state exception is thrown: @@ -29,5 +36,41 @@ fun convertibleToBitmap( } ) - return { composeView.drawToBitmap() } + return { + createBitmapFromView( + view = composeView, + width = composeView.width, + height = (composeView.width * getAspectRatioForBitmap(context)).toInt() + ) + } } + +fun createBitmapFromView(view: View, width: Int, height: Int): Bitmap { + view.layoutParams = LinearLayoutCompat.LayoutParams( + LinearLayoutCompat.LayoutParams.WRAP_CONTENT, + LinearLayoutCompat.LayoutParams.WRAP_CONTENT + ) + + view.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY) + ) + + view.layout(0, 0, width, height) + + val canvas = Canvas() + val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + + canvas.setBitmap(bitmap) + view.draw(canvas) + + return bitmap +} + +private fun getAspectRatioForBitmap(context: Context) = + if (Util.isTablet(context)) { + StoriesViewAspectRatioForTablet + } else { + val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager? + wm?.deviceAspectRatio() + } ?: StoriesViewAspectRatioForTablet diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryIntroView.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryIntroView.kt index 4ccba746027..fa0fb6f4479 100644 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryIntroView.kt +++ b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryIntroView.kt @@ -1,31 +1,68 @@ package au.com.shiftyjelly.pocketcasts.endofyear.views.stories +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +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.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.Surface import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign 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.components.TextH30 +import au.com.shiftyjelly.pocketcasts.compose.components.TextH20 import au.com.shiftyjelly.pocketcasts.compose.preview.ThemePreviewParameterProvider +import au.com.shiftyjelly.pocketcasts.endofyear.R import au.com.shiftyjelly.pocketcasts.endofyear.stories.StoryIntro import au.com.shiftyjelly.pocketcasts.ui.theme.Theme +import au.com.shiftyjelly.pocketcasts.localization.R as LR @Composable fun StoryIntroView( story: StoryIntro, modifier: Modifier = Modifier, ) { - TextH30( - text = "Let's celebrate your year of listening..", - color = story.tintColor, - textAlign = TextAlign.Center, - modifier = modifier.padding(16.dp) - ) + Column( + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + ) { + Spacer(modifier = modifier.height(40.dp)) + Image( + painter = painterResource(R.drawable.img_2022_big), + contentDescription = null, + modifier = modifier.fillMaxWidth(), + contentScale = ContentScale.FillBounds + ) + Spacer(modifier = modifier.weight(0.4f)) + TextH20( + text = stringResource(id = LR.string.end_of_year_story_intro_title), + color = story.tintColor, + textAlign = TextAlign.Center, + modifier = modifier.padding(horizontal = 35.dp) + ) + Spacer(modifier = modifier.weight(1f)) + Image( + painter = painterResource(R.drawable.logo_white), + contentDescription = null, + modifier = modifier.padding(bottom = 40.dp) + ) + } } @Preview(showBackground = true) diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryTopFivePodcastsView.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryTopFivePodcastsView.kt index 9a0d26555ed..b2eb25dc371 100644 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryTopFivePodcastsView.kt +++ b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryTopFivePodcastsView.kt @@ -1,45 +1,27 @@ package au.com.shiftyjelly.pocketcasts.endofyear.views.stories -import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable -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.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.coerceAtMost import androidx.compose.ui.unit.dp import au.com.shiftyjelly.pocketcasts.compose.components.PodcastItem import au.com.shiftyjelly.pocketcasts.compose.components.TextH30 import au.com.shiftyjelly.pocketcasts.endofyear.stories.StoryTopFivePodcasts -import au.com.shiftyjelly.pocketcasts.utils.extensions.pxToDp @Composable fun StoryTopFivePodcastsView( story: StoryTopFivePodcasts, modifier: Modifier = Modifier, ) { - val context = LocalContext.current - var screenHeight by remember { mutableStateOf(1) } - var textFieldHeight by remember { mutableStateOf(1) } Column( - modifier = modifier - .fillMaxSize() - .onGloballyPositioned { - screenHeight = it.size.height - }, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, + modifier = modifier.verticalScroll(rememberScrollState()), ) { TextH30( text = "Your Top Podcasts", @@ -48,34 +30,14 @@ fun StoryTopFivePodcastsView( modifier = modifier .fillMaxWidth() .padding(vertical = 16.dp) - .onGloballyPositioned { - textFieldHeight = it.size.height - } ) - LazyColumn( - modifier = modifier.fillMaxWidth(), - verticalArrangement = Arrangement.Center - ) { - items(story.topPodcasts) { topPodcast -> - PodcastItem( - podcast = topPodcast.toPodcast(), - iconSize = getIconSize(screenHeight, textFieldHeight, context), - onClick = {}, - tintColor = story.tintColor, - showDivider = false - ) - } + story.topPodcasts.forEach { topPodcast -> + PodcastItem( + podcast = topPodcast.toPodcast(), + onClick = null, + tintColor = story.tintColor, + showDivider = false + ) } } } - -fun getIconSize( - screenHeight: Int, - textFieldHeight: Int, - context: Context, -): Dp { - return screenHeight.pxToDp(context).dp - .minus(32.dp + textFieldHeight.dp) - .div(5) - .coerceAtMost(64.dp) -} diff --git a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryTopListenedCategoriesView.kt b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryTopListenedCategoriesView.kt index b808368b087..6b6165bfc42 100644 --- a/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryTopListenedCategoriesView.kt +++ b/modules/features/endofyear/src/main/java/au/com/shiftyjelly/pocketcasts/endofyear/views/stories/StoryTopListenedCategoriesView.kt @@ -1,29 +1,22 @@ package au.com.shiftyjelly.pocketcasts.endofyear.views.stories import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Arrangement 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.Surface import androidx.compose.runtime.Composable -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.graphics.Color -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import au.com.shiftyjelly.pocketcasts.compose.AppTheme import au.com.shiftyjelly.pocketcasts.compose.components.TextH30 @@ -39,17 +32,7 @@ fun StoryTopListenedCategoriesView( story: StoryTopListenedCategories, modifier: Modifier = Modifier, ) { - val context = LocalContext.current - var screenHeight by remember { mutableStateOf(1) } - var textFieldHeight by remember { mutableStateOf(1) } - Column( - modifier = modifier - .fillMaxSize() - .onGloballyPositioned { - screenHeight = it.size.height - }, - verticalArrangement = Arrangement.Center - ) { + Column(modifier = modifier.verticalScroll(rememberScrollState())) { TextH30( text = "Your Top Categories", textAlign = TextAlign.Center, @@ -57,9 +40,6 @@ fun StoryTopListenedCategoriesView( modifier = modifier .fillMaxWidth() .padding(vertical = 16.dp) - .onGloballyPositioned { - textFieldHeight = it.size.height - } ) story.listenedCategories.subList(0, minOf(story.listenedCategories.count(), 5)) .mapIndexed { index, listenedCategory -> @@ -67,7 +47,6 @@ fun StoryTopListenedCategoriesView( listenedCategory = listenedCategory, position = index, tintColor = story.tintColor, - iconSize = getIconSize(screenHeight, textFieldHeight, context) ) } } @@ -78,7 +57,6 @@ fun CategoryItem( listenedCategory: ListenedCategory, position: Int, tintColor: Color, - iconSize: Dp, modifier: Modifier = Modifier, ) { Column { @@ -103,7 +81,7 @@ fun CategoryItem( painter = painterResource(IR.drawable.defaultartwork), contentDescription = null, modifier = modifier - .size(iconSize) + .size(64.dp) .padding(top = 4.dp, end = 12.dp, bottom = 4.dp) ) TextP40( @@ -144,7 +122,6 @@ private fun CategoryItemPreview( ), position = 0, tintColor = Color.White, - iconSize = 64.dp, ) } } diff --git a/modules/features/endofyear/src/main/res/drawable/img_2022_big.xml b/modules/features/endofyear/src/main/res/drawable/img_2022_big.xml new file mode 100644 index 00000000000..cf2999ecf2c --- /dev/null +++ b/modules/features/endofyear/src/main/res/drawable/img_2022_big.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/features/endofyear/src/main/res/drawable/logo_white.xml b/modules/features/endofyear/src/main/res/drawable/logo_white.xml new file mode 100644 index 00000000000..2bd0fb98a90 --- /dev/null +++ b/modules/features/endofyear/src/main/res/drawable/logo_white.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + diff --git a/modules/features/profile/src/main/java/au/com/shiftyjelly/pocketcasts/profile/ProfileFragment.kt b/modules/features/profile/src/main/java/au/com/shiftyjelly/pocketcasts/profile/ProfileFragment.kt index 37f44e68bfc..e6aa37b45ed 100644 --- a/modules/features/profile/src/main/java/au/com/shiftyjelly/pocketcasts/profile/ProfileFragment.kt +++ b/modules/features/profile/src/main/java/au/com/shiftyjelly/pocketcasts/profile/ProfileFragment.kt @@ -10,6 +10,10 @@ import android.view.ViewGroup import android.widget.TextView import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat import androidx.core.view.isInvisible @@ -27,7 +31,8 @@ import au.com.shiftyjelly.pocketcasts.account.AccountActivity import au.com.shiftyjelly.pocketcasts.analytics.AnalyticsEvent import au.com.shiftyjelly.pocketcasts.analytics.AnalyticsTrackerWrapper import au.com.shiftyjelly.pocketcasts.compose.AppTheme -import au.com.shiftyjelly.pocketcasts.endofyear.StoriesFragment +import au.com.shiftyjelly.pocketcasts.endofyear.StoriesPage +import au.com.shiftyjelly.pocketcasts.endofyear.StoriesViewModel import au.com.shiftyjelly.pocketcasts.endofyear.views.EndOfYearPromptCard import au.com.shiftyjelly.pocketcasts.localization.extensions.getStringPluralSecondsMinutesHoursDaysOrYears import au.com.shiftyjelly.pocketcasts.models.to.RefreshState @@ -69,6 +74,8 @@ class ProfileFragment : BaseFragment() { @Inject lateinit var analyticsTracker: AnalyticsTrackerWrapper private val viewModel: ProfileViewModel by viewModels() + private val storiesViewModel: StoriesViewModel by viewModels() + private var binding: FragmentProfileBinding? = null private val sections = listOf( SettingsAdapter.Item(LR.string.profile_navigation_stats, R.drawable.ic_stats, StatsFragment::class.java), @@ -234,11 +241,19 @@ class ProfileFragment : BaseFragment() { private fun FragmentProfileBinding.setupEndOfYearPromptCard(isEligible: Boolean) { endOfYearPromptCard.setContent { if (isEligible) { + var showDialog by rememberSaveable { mutableStateOf(false) } + if (showDialog) { + StoriesPage( + viewModel = storiesViewModel, + showDialog = showDialog, + theme = theme, + onCloseClicked = { showDialog = false }, + ) + } AppTheme(theme.activeTheme) { EndOfYearPromptCard( onClick = { - StoriesFragment.newInstance() - .show(childFragmentManager, "stories_dialog") + showDialog = true } ) } diff --git a/modules/services/localization/src/main/res/values/strings.xml b/modules/services/localization/src/main/res/values/strings.xml index c1b0a306565..cc1a9b59ead 100644 --- a/modules/services/localization/src/main/res/values/strings.xml +++ b/modules/services/localization/src/main/res/values/strings.xml @@ -1403,6 +1403,8 @@ In 2022, you spent %1$s listening to podcasts. Replay + Let\'s celebrate your year of listening... + Must provide filter name diff --git a/modules/services/ui/src/main/res/values/themes.xml b/modules/services/ui/src/main/res/values/themes.xml index 31486c47463..b4add37711b 100644 --- a/modules/services/ui/src/main/res/values/themes.xml +++ b/modules/services/ui/src/main/res/values/themes.xml @@ -1657,15 +1657,6 @@ @drawable/bg_bottom_sheet_dialog_fragment_dark - - - diff --git a/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/Util.kt b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/Util.kt index 6b36d323699..a5dde8f3efa 100644 --- a/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/Util.kt +++ b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/Util.kt @@ -13,7 +13,7 @@ import android.view.accessibility.AccessibilityManager import java.util.Locale object Util { - private const val MINIMUM_SMALLEST_WIDTH_DP_FOR_TABLET = 600 + private const val MINIMUM_SMALLEST_WIDTH_DP_FOR_TABLET = 570 fun isCarUiMode(context: Context): Boolean { val uiModeManager = context.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager diff --git a/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/extensions/WindowManager.kt b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/extensions/WindowManager.kt new file mode 100644 index 00000000000..3060ea3be17 --- /dev/null +++ b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/extensions/WindowManager.kt @@ -0,0 +1,15 @@ +package au.com.shiftyjelly.pocketcasts.utils.extensions + +import android.graphics.Point +import android.os.Build +import android.view.WindowManager + +fun WindowManager.deviceAspectRatio() = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + (maximumWindowMetrics.bounds.height() / maximumWindowMetrics.bounds.width().toFloat()) + } else { + val size = Point() + @Suppress("DEPRECATION") + defaultDisplay.getRealSize(size) + size.y / size.x.toFloat() + }