From 5192a0bb0fc54a5c6a0d59c923622fd27f90b3e8 Mon Sep 17 00:00:00 2001 From: ashiagr Date: Mon, 21 Oct 2024 18:29:43 +0530 Subject: [PATCH 1/6] Add feature flag --- .../shiftyjelly/pocketcasts/utils/featureflag/Feature.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/featureflag/Feature.kt b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/featureflag/Feature.kt index 59a184a915..5821c64c24 100644 --- a/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/featureflag/Feature.kt +++ b/modules/services/utils/src/main/java/au/com/shiftyjelly/pocketcasts/utils/featureflag/Feature.kt @@ -131,6 +131,14 @@ enum class Feature( hasFirebaseRemoteFlag = false, hasDevToggle = true, ), + CUSTOM_PLAYBACK_SETTINGS( + key = "custom_playback_settings", + title = "Custom playback settings", + defaultValue = BuildConfig.DEBUG, + tier = FeatureTier.Free, + hasFirebaseRemoteFlag = false, + hasDevToggle = true, + ), ; companion object { From 1430b70e257fe3c44fae4e8214502acfddcd5231 Mon Sep 17 00:00:00 2001 From: ashiagr Date: Mon, 21 Oct 2024 18:49:52 +0530 Subject: [PATCH 2/6] Rename and update segmented tab bar style --- .../upgrade/OnboardingUpgradeFeaturesPage.kt | 15 +- .../compose/components/SegmentedTabBar.kt | 144 ++++++++++++++++++ .../compose/components/StyledToggle.kt | 70 --------- 3 files changed, 153 insertions(+), 76 deletions(-) create mode 100644 modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt delete mode 100644 modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/StyledToggle.kt diff --git a/modules/features/account/src/main/java/au/com/shiftyjelly/pocketcasts/account/onboarding/upgrade/OnboardingUpgradeFeaturesPage.kt b/modules/features/account/src/main/java/au/com/shiftyjelly/pocketcasts/account/onboarding/upgrade/OnboardingUpgradeFeaturesPage.kt index eb4ec13077..70a2e590fa 100644 --- a/modules/features/account/src/main/java/au/com/shiftyjelly/pocketcasts/account/onboarding/upgrade/OnboardingUpgradeFeaturesPage.kt +++ b/modules/features/account/src/main/java/au/com/shiftyjelly/pocketcasts/account/onboarding/upgrade/OnboardingUpgradeFeaturesPage.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.BoxWithConstraintsScope import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -71,7 +72,7 @@ import au.com.shiftyjelly.pocketcasts.compose.bars.NavigationIconButton import au.com.shiftyjelly.pocketcasts.compose.bars.SystemBarsStyles import au.com.shiftyjelly.pocketcasts.compose.components.AutoResizeText import au.com.shiftyjelly.pocketcasts.compose.components.HorizontalPagerWrapper -import au.com.shiftyjelly.pocketcasts.compose.components.StyledToggle +import au.com.shiftyjelly.pocketcasts.compose.components.SegmentedTabBar import au.com.shiftyjelly.pocketcasts.compose.components.TextH30 import au.com.shiftyjelly.pocketcasts.compose.images.OfferBadge import au.com.shiftyjelly.pocketcasts.compose.images.SubscriptionBadge @@ -255,16 +256,18 @@ private fun UpgradeLayoutOriginal( .padding(bottom = 24.dp), contentAlignment = Alignment.Center, ) { - StyledToggle( + SegmentedTabBar( items = state.subscriptionFrequencies .map { stringResource(id = it.localisedLabelRes) }, defaultSelectedItemIndex = state.subscriptionFrequencies.indexOf( state.currentSubscriptionFrequency, ), - ) { - val selectedFrequency = state.subscriptionFrequencies[it] - onSubscriptionFrequencyChanged(selectedFrequency) - } + onItemSelected = { + val selectedFrequency = state.subscriptionFrequencies[it] + onSubscriptionFrequencyChanged(selectedFrequency) + }, + modifier = Modifier.width(IntrinsicSize.Max), + ) } FeatureCards( diff --git a/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt b/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt new file mode 100644 index 0000000000..987bfe6a2a --- /dev/null +++ b/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt @@ -0,0 +1,144 @@ +package au.com.shiftyjelly.pocketcasts.compose.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement +import androidx.compose.material.OutlinedButton +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +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.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import au.com.shiftyjelly.pocketcasts.compose.AppThemeWithBackground +import au.com.shiftyjelly.pocketcasts.ui.theme.Theme +import com.airbnb.android.showkase.annotation.ShowkaseComposable +import au.com.shiftyjelly.pocketcasts.localization.R as LR + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun SegmentedTabBar( + items: List, + modifier: Modifier = Modifier, + defaultSelectedItemIndex: Int = 0, + colors: SegmentedTabBarColors = SegmentedTabBarDefaults.colors, + cornerRadius: Dp = SegmentedTabBarDefaults.cornerRadius, + outerPadding: Dp = SegmentedTabBarDefaults.outerPadding, + tabContentPadding: PaddingValues = SegmentedTabBarDefaults.tabContentPadding, + textStyle: TextStyle = SegmentedTabBarDefaults.textStyle, + onItemSelected: (selectedItemIndex: Int) -> Unit, +) { + var selectedIndex by remember { mutableIntStateOf(defaultSelectedItemIndex) } + + Box( + modifier = modifier + .clip(RoundedCornerShape(cornerRadius)) + .background(colors.baseBackgroundColor) + .padding(outerPadding), + ) { + Row( + horizontalArrangement = Arrangement.Absolute.SpaceBetween, + ) { + items.forEachIndexed { index, item -> + CompositionLocalProvider( + LocalMinimumInteractiveComponentEnforcement provides false, + ) { + OutlinedButton( + shape = RoundedCornerShape(cornerRadius), + border = null, + colors = ButtonDefaults.buttonColors( + backgroundColor = if (selectedIndex == index) colors.selectedTabBackgroundColor else colors.unSelectedTabBackgroundColor, + ), + contentPadding = tabContentPadding, + onClick = { + selectedIndex = index + onItemSelected(selectedIndex) + }, + modifier = Modifier + .weight(1f) + .defaultMinSize(minWidth = 1.dp, minHeight = 1.dp), + ) { + Text( + text = item, + color = if (selectedIndex == index) colors.selectedTabTextColor else colors.unSelectedTabTextColor, + fontSize = textStyle.fontSize, + letterSpacing = textStyle.letterSpacing, + lineHeight = 18.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontWeight = FontWeight.W500, + ) + } + } + } + } + } +} + +object SegmentedTabBarDefaults { + val colors = SegmentedTabBarColors() + val cornerRadius: Dp = 24.dp + val outerPadding: Dp = 4.dp + val tabContentPadding: PaddingValues = ButtonDefaults.ContentPadding + val textStyle = TextStyle( + fontSize = 15.sp, + letterSpacing = -(0.08).sp, + ) +} + +data class SegmentedTabBarColors( + val baseBackgroundColor: Color = Color.White.copy(alpha = .1f), + val selectedTabBackgroundColor: Color = Color.White, + val selectedTabTextColor: Color = Color.Black, + val unSelectedTabBackgroundColor: Color = Color.Transparent, + val unSelectedTabTextColor: Color = Color.White, +) + +@ShowkaseComposable(name = "SegmentedTabBar", group = "TabBar", styleName = "Compact", defaultStyle = true) +@Preview +@Composable +fun SegmentedTabBarCompactPreview() { + AppThemeWithBackground(Theme.ThemeType.DARK) { + SegmentedTabBar( + items = listOf(stringResource(LR.string.plus_yearly), stringResource(LR.string.plus_monthly)), + onItemSelected = {}, + modifier = Modifier.width(IntrinsicSize.Max), + ) + } +} + +@ShowkaseComposable(name = "SegmentedTabBar", group = "TabBar") +@Preview +@Composable +fun SegmentedTabBarFillWidthPreview() { + AppThemeWithBackground(Theme.ThemeType.DARK) { + SegmentedTabBar( + items = listOf(stringResource(LR.string.plus_yearly), stringResource(LR.string.plus_monthly)), + onItemSelected = {}, + modifier = Modifier.fillMaxWidth(), + ) + } +} diff --git a/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/StyledToggle.kt b/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/StyledToggle.kt deleted file mode 100644 index c98248ec76..0000000000 --- a/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/StyledToggle.kt +++ /dev/null @@ -1,70 +0,0 @@ -package au.com.shiftyjelly.pocketcasts.compose.components - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ButtonDefaults -import androidx.compose.material.OutlinedButton -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.draw.clip -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -private val CornerRadiusInDp = 24.dp - -@Composable -fun StyledToggle( - items: List, - modifier: Modifier = Modifier, - defaultSelectedItemIndex: Int = 0, - cornerRadius: Dp = CornerRadiusInDp, - onItemSelected: (selectedItemIndex: Int) -> Unit, -) { - var selectedIndex by remember { mutableStateOf(defaultSelectedItemIndex) } - - Box( - modifier = modifier - .clip(RoundedCornerShape(cornerRadius)) - .background(Color.White.copy(alpha = .16f)) - .padding(horizontal = 8.dp), - ) { - Row { - items.forEachIndexed { index, item -> - OutlinedButton( - shape = RoundedCornerShape(cornerRadius), - border = null, - colors = ButtonDefaults.buttonColors( - backgroundColor = if (selectedIndex == index) Color.White else Color.Transparent, - ), - onClick = { - selectedIndex = index - onItemSelected(selectedIndex) - }, - ) { - TextH40( - text = item, - color = if (selectedIndex == index) Color.Black else Color.White, - ) - } - } - } - } -} - -@Preview -@Composable -private fun OnboardingPatonFeatureCardPreview() { - StyledToggle( - items = listOf("Yearly", "Monthly"), - onItemSelected = {}, - ) -} From ab2ba2f607f928bbe713bc45ee7f44abad27de1a Mon Sep 17 00:00:00 2001 From: ashiagr Date: Mon, 21 Oct 2024 18:50:54 +0530 Subject: [PATCH 3/6] Display playback effects segmented control --- .../player/view/EffectsFragment.kt | 69 +++++++++++++++++++ .../src/main/res/layout/fragment_effects.xml | 16 ++++- .../src/main/res/values/strings.xml | 1 + 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt index f401517b74..c0345528ca 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt @@ -7,11 +7,29 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.CompoundButton +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.graphics.toColorInt import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.fragment.app.activityViewModels +import androidx.lifecycle.asFlow +import androidx.lifecycle.compose.collectAsStateWithLifecycle import au.com.shiftyjelly.pocketcasts.analytics.AnalyticsEvent import au.com.shiftyjelly.pocketcasts.analytics.SourceView +import au.com.shiftyjelly.pocketcasts.compose.components.SegmentedTabBar +import au.com.shiftyjelly.pocketcasts.compose.components.SegmentedTabBarDefaults +import au.com.shiftyjelly.pocketcasts.compose.theme import au.com.shiftyjelly.pocketcasts.localization.helper.TimeHelper import au.com.shiftyjelly.pocketcasts.models.entity.Podcast import au.com.shiftyjelly.pocketcasts.models.to.PlaybackEffects @@ -25,10 +43,13 @@ import au.com.shiftyjelly.pocketcasts.repositories.images.loadInto import au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackManager import au.com.shiftyjelly.pocketcasts.repositories.user.StatsManager import au.com.shiftyjelly.pocketcasts.ui.extensions.themed +import au.com.shiftyjelly.pocketcasts.ui.helper.ColorUtils import au.com.shiftyjelly.pocketcasts.ui.helper.StatusBarColor import au.com.shiftyjelly.pocketcasts.ui.theme.ThemeColor import au.com.shiftyjelly.pocketcasts.utils.extensions.dpToPx import au.com.shiftyjelly.pocketcasts.utils.extensions.roundedSpeed +import au.com.shiftyjelly.pocketcasts.utils.featureflag.Feature +import au.com.shiftyjelly.pocketcasts.utils.featureflag.FeatureFlag import au.com.shiftyjelly.pocketcasts.views.extensions.applyColor import au.com.shiftyjelly.pocketcasts.views.extensions.updateTint import au.com.shiftyjelly.pocketcasts.views.fragments.BaseDialogFragment @@ -94,6 +115,10 @@ class EffectsFragment : BaseDialogFragment(), CompoundButton.OnCheckedChangeList imageRequestFactory.create(podcast).loadInto(binding.podcastEffectsImage) + if (FeatureFlag.isEnabled(Feature.CUSTOM_PLAYBACK_SETTINGS)) { + binding.setupEffectsSettingsSegmentedTabBar() + } + binding.lblSpeed.text = String.format("%.1fx", effects.playbackSpeed) binding.btnSpeedUp.setOnClickListener(this) @@ -246,4 +271,48 @@ class EffectsFragment : BaseDialogFragment(), CompoundButton.OnCheckedChangeList private fun trackPlaybackEffectsEvent(event: AnalyticsEvent, props: Map = emptyMap()) { playbackManager.trackPlaybackEffectsEvent(event, props, SourceView.PLAYER_PLAYBACK_EFFECTS) } + + private fun FragmentEffectsBinding.setupEffectsSettingsSegmentedTabBar() { + effectsSettingsSegmentedTabBar.setContent { + val playingEpisodeState by viewModel.playingEpisodeLive.asFlow().collectAsStateWithLifecycle(null) + playingEpisodeState?.let { + val (_, podcastHeaderBackgroundColor) = it + EffectsSettingsSegmentedTabBar( + modifier = Modifier + .padding(top = 24.dp), + selectedTabTextColor = Color(ColorUtils.colorIntToHexString(podcastHeaderBackgroundColor).toColorInt()), + ) + } + } + } + + @Composable + private fun EffectsSettingsSegmentedTabBar( + modifier: Modifier = Modifier, + selectedTabTextColor: Color = Color.Black, + ) { + SegmentedTabBar( + items = listOf(stringResource(LR.string.podcasts_all), stringResource(LR.string.podcast_this)), + defaultSelectedItemIndex = 0, + colors = SegmentedTabBarDefaults.colors.copy( + baseBackgroundColor = MaterialTheme.theme.colors.playerContrast06.copy(alpha = .1f), + selectedTabTextColor = selectedTabTextColor, + unSelectedTabTextColor = MaterialTheme.theme.colors.playerContrast02.copy(alpha = .5f), + ), + cornerRadius = 7.dp, + outerPadding = 2.dp, + tabContentPadding = PaddingValues(horizontal = 10.dp, vertical = 3.dp), + textStyle = SegmentedTabBarDefaults.textStyle.copy( + fontSize = 13.sp, + ), + modifier = modifier.fillMaxWidth(), + onItemSelected = {}, + ) + } + + @Preview(widthDp = 360) + @Composable + private fun EffectsSettingsSegmentedBarPreview() { + EffectsSettingsSegmentedTabBar() + } } diff --git a/modules/features/player/src/main/res/layout/fragment_effects.xml b/modules/features/player/src/main/res/layout/fragment_effects.xml index 30ef397ca6..e29a3c0feb 100644 --- a/modules/features/player/src/main/res/layout/fragment_effects.xml +++ b/modules/features/player/src/main/res/layout/fragment_effects.xml @@ -32,13 +32,23 @@ style="@style/H30" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginStart="32dp" + android:layout_marginStart="16dp" android:layout_marginTop="24dp" android:text="@string/podcast_playback_effects" android:textColor="?attr/player_contrast_01" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"/> + + + app:layout_constraintLeft_toLeftOf="@id/effectsSettingsSegmentedTabBar" + app:layout_constraintTop_toBottomOf="@id/effectsSettingsSegmentedTabBar" /> Support cancelled Manage Thanks for your support + This podcast Unable to update this feed, please try again later. Update Failed We\'ve queued an update for this podcast. Our server will re-check it and if it works you should have new episodes soon. Please check back in about an hour. From f9337c10d6f19ca747ee4ad35a0e41a459217c4d Mon Sep 17 00:00:00 2001 From: ashiagr Date: Mon, 21 Oct 2024 19:12:02 +0530 Subject: [PATCH 4/6] Fix accessibility --- .../pocketcasts/compose/components/SegmentedTabBar.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt b/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt index 987bfe6a2a..a8cd2318de 100644 --- a/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt +++ b/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt @@ -26,6 +26,9 @@ 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.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow @@ -79,7 +82,8 @@ fun SegmentedTabBar( }, modifier = Modifier .weight(1f) - .defaultMinSize(minWidth = 1.dp, minHeight = 1.dp), + .defaultMinSize(minWidth = 1.dp, minHeight = 1.dp) + .semantics { role = Role.Tab }, ) { Text( text = item, From 433eddbf87cf034d2af18a6e60abbd2147269832 Mon Sep 17 00:00:00 2001 From: ashiagr Date: Tue, 22 Oct 2024 13:33:46 +0530 Subject: [PATCH 5/6] Move setup to onCreateView --- .../pocketcasts/player/view/EffectsFragment.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt index c0345528ca..478407d67d 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt @@ -84,6 +84,10 @@ class EffectsFragment : BaseDialogFragment(), CompoundButton.OnCheckedChangeList override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { binding = FragmentEffectsBinding.inflate(inflater, container, false) + if (FeatureFlag.isEnabled(Feature.CUSTOM_PLAYBACK_SETTINGS)) { + binding?.setupEffectsSettingsSegmentedTabBar() + } + viewModel.effectsLive.value?.let { update(it) } // Make sure the window is the correct size before opening or else it won't expand properly viewModel.effectsLive.observe(viewLifecycleOwner) { podcastEffectsPair -> update(podcastEffectsPair) @@ -115,10 +119,6 @@ class EffectsFragment : BaseDialogFragment(), CompoundButton.OnCheckedChangeList imageRequestFactory.create(podcast).loadInto(binding.podcastEffectsImage) - if (FeatureFlag.isEnabled(Feature.CUSTOM_PLAYBACK_SETTINGS)) { - binding.setupEffectsSettingsSegmentedTabBar() - } - binding.lblSpeed.text = String.format("%.1fx", effects.playbackSpeed) binding.btnSpeedUp.setOnClickListener(this) From 64c14cf3196697d5d54d31739394ef776a339d16 Mon Sep 17 00:00:00 2001 From: ashiagr Date: Thu, 24 Oct 2024 15:32:16 +0530 Subject: [PATCH 6/6] Update style --- .../player/view/EffectsFragment.kt | 30 +--- .../compose/components/SegmentedTabBar.kt | 148 +++++++++++------- 2 files changed, 98 insertions(+), 80 deletions(-) diff --git a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt index 478407d67d..dc7f945c94 100644 --- a/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt +++ b/modules/features/player/src/main/java/au/com/shiftyjelly/pocketcasts/player/view/EffectsFragment.kt @@ -7,24 +7,18 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.CompoundButton -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.core.graphics.toColorInt import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.fragment.app.activityViewModels -import androidx.lifecycle.asFlow -import androidx.lifecycle.compose.collectAsStateWithLifecycle import au.com.shiftyjelly.pocketcasts.analytics.AnalyticsEvent import au.com.shiftyjelly.pocketcasts.analytics.SourceView import au.com.shiftyjelly.pocketcasts.compose.components.SegmentedTabBar @@ -43,7 +37,6 @@ import au.com.shiftyjelly.pocketcasts.repositories.images.loadInto import au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackManager import au.com.shiftyjelly.pocketcasts.repositories.user.StatsManager import au.com.shiftyjelly.pocketcasts.ui.extensions.themed -import au.com.shiftyjelly.pocketcasts.ui.helper.ColorUtils import au.com.shiftyjelly.pocketcasts.ui.helper.StatusBarColor import au.com.shiftyjelly.pocketcasts.ui.theme.ThemeColor import au.com.shiftyjelly.pocketcasts.utils.extensions.dpToPx @@ -274,34 +267,25 @@ class EffectsFragment : BaseDialogFragment(), CompoundButton.OnCheckedChangeList private fun FragmentEffectsBinding.setupEffectsSettingsSegmentedTabBar() { effectsSettingsSegmentedTabBar.setContent { - val playingEpisodeState by viewModel.playingEpisodeLive.asFlow().collectAsStateWithLifecycle(null) - playingEpisodeState?.let { - val (_, podcastHeaderBackgroundColor) = it - EffectsSettingsSegmentedTabBar( - modifier = Modifier - .padding(top = 24.dp), - selectedTabTextColor = Color(ColorUtils.colorIntToHexString(podcastHeaderBackgroundColor).toColorInt()), - ) - } + EffectsSettingsSegmentedTabBar( + modifier = Modifier + .padding(top = 24.dp), + ) } } @Composable private fun EffectsSettingsSegmentedTabBar( modifier: Modifier = Modifier, - selectedTabTextColor: Color = Color.Black, ) { SegmentedTabBar( items = listOf(stringResource(LR.string.podcasts_all), stringResource(LR.string.podcast_this)), defaultSelectedItemIndex = 0, colors = SegmentedTabBarDefaults.colors.copy( - baseBackgroundColor = MaterialTheme.theme.colors.playerContrast06.copy(alpha = .1f), - selectedTabTextColor = selectedTabTextColor, - unSelectedTabTextColor = MaterialTheme.theme.colors.playerContrast02.copy(alpha = .5f), + selectedTabBackgroundColor = MaterialTheme.theme.colors.playerContrast06.copy(alpha = .1f), + borderColor = MaterialTheme.theme.colors.playerContrast03.copy(alpha = .4f), ), - cornerRadius = 7.dp, - outerPadding = 2.dp, - tabContentPadding = PaddingValues(horizontal = 10.dp, vertical = 3.dp), + cornerRadius = 120.dp, textStyle = SegmentedTabBarDefaults.textStyle.copy( fontSize = 13.sp, ), diff --git a/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt b/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt index a8cd2318de..fa4f1adf6b 100644 --- a/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt +++ b/modules/services/compose/src/main/java/au/com/shiftyjelly/pocketcasts/compose/components/SegmentedTabBar.kt @@ -1,21 +1,26 @@ package au.com.shiftyjelly.pocketcasts.compose.components +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.IntrinsicSize -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ButtonDefaults import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Icon import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement import androidx.compose.material.OutlinedButton +import androidx.compose.material.Surface import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue @@ -23,8 +28,8 @@ import androidx.compose.runtime.mutableIntStateOf 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.graphics.RectangleShape import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.role @@ -49,52 +54,37 @@ fun SegmentedTabBar( defaultSelectedItemIndex: Int = 0, colors: SegmentedTabBarColors = SegmentedTabBarDefaults.colors, cornerRadius: Dp = SegmentedTabBarDefaults.cornerRadius, - outerPadding: Dp = SegmentedTabBarDefaults.outerPadding, - tabContentPadding: PaddingValues = SegmentedTabBarDefaults.tabContentPadding, + border: BorderStroke = BorderStroke(SegmentedTabBarDefaults.borderThickness, colors.borderColor), textStyle: TextStyle = SegmentedTabBarDefaults.textStyle, onItemSelected: (selectedItemIndex: Int) -> Unit, ) { var selectedIndex by remember { mutableIntStateOf(defaultSelectedItemIndex) } - - Box( + Surface( + shape = RoundedCornerShape(cornerRadius), + border = border, + color = Color.Transparent, modifier = modifier - .clip(RoundedCornerShape(cornerRadius)) - .background(colors.baseBackgroundColor) - .padding(outerPadding), + .height(SegmentedTabBarDefaults.height), ) { Row( horizontalArrangement = Arrangement.Absolute.SpaceBetween, ) { - items.forEachIndexed { index, item -> + items.forEachIndexed { index, text -> CompositionLocalProvider( LocalMinimumInteractiveComponentEnforcement provides false, ) { - OutlinedButton( - shape = RoundedCornerShape(cornerRadius), - border = null, - colors = ButtonDefaults.buttonColors( - backgroundColor = if (selectedIndex == index) colors.selectedTabBackgroundColor else colors.unSelectedTabBackgroundColor, - ), - contentPadding = tabContentPadding, - onClick = { + SegmentedTab( + isSelected = selectedIndex == index, + text = text, + textStyle = textStyle, + colors = colors, + onItemSelected = { selectedIndex = index - onItemSelected(selectedIndex) + onItemSelected(index) }, - modifier = Modifier - .weight(1f) - .defaultMinSize(minWidth = 1.dp, minHeight = 1.dp) - .semantics { role = Role.Tab }, - ) { - Text( - text = item, - color = if (selectedIndex == index) colors.selectedTabTextColor else colors.unSelectedTabTextColor, - fontSize = textStyle.fontSize, - letterSpacing = textStyle.letterSpacing, - lineHeight = 18.sp, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - fontWeight = FontWeight.W500, - ) + ) + if (index < items.size - 1) { + Divider(colors.borderColor) } } } @@ -102,47 +92,91 @@ fun SegmentedTabBar( } } +@Composable +private fun RowScope.SegmentedTab( + isSelected: Boolean, + text: String, + textStyle: TextStyle, + colors: SegmentedTabBarColors, + onItemSelected: () -> Unit, +) { + OutlinedButton( + shape = RectangleShape, + border = null, + colors = ButtonDefaults.buttonColors( + backgroundColor = if (isSelected) colors.selectedTabBackgroundColor else colors.unSelectedTabBackgroundColor, + ), + onClick = onItemSelected, + modifier = Modifier.Companion + .weight(1f) + .fillMaxSize() + .defaultMinSize(minWidth = SegmentedTabBarDefaults.tabMinSize) + .semantics { role = Role.Tab }, + ) { + if (isSelected) { + Icon( + imageVector = Icons.Filled.Check, + contentDescription = null, + tint = colors.selectedTabIconColor, + ) + Spacer(modifier = Modifier.width(8.dp)) + } + Text( + text = text, + color = if (isSelected) colors.selectedTabTextColor else colors.unSelectedTabTextColor, + fontSize = textStyle.fontSize, + letterSpacing = textStyle.letterSpacing, + lineHeight = 18.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontWeight = FontWeight.W500, + ) + } +} + +@Composable +private fun Divider( + color: Color, +) { + Spacer( + modifier = Modifier + .background(color) + .width(SegmentedTabBarDefaults.borderThickness) + .height(SegmentedTabBarDefaults.height), + ) +} + object SegmentedTabBarDefaults { val colors = SegmentedTabBarColors() val cornerRadius: Dp = 24.dp - val outerPadding: Dp = 4.dp - val tabContentPadding: PaddingValues = ButtonDefaults.ContentPadding + val borderThickness = 1.0.dp val textStyle = TextStyle( fontSize = 15.sp, letterSpacing = -(0.08).sp, ) + val height: Dp = 40.dp + val tabMinSize: Dp = 150.dp } data class SegmentedTabBarColors( - val baseBackgroundColor: Color = Color.White.copy(alpha = .1f), - val selectedTabBackgroundColor: Color = Color.White, - val selectedTabTextColor: Color = Color.Black, + val selectedTabBackgroundColor: Color = Color.White.copy(alpha = .1f), + val selectedTabTextColor: Color = Color.White, + val selectedTabIconColor: Color = Color.White, val unSelectedTabBackgroundColor: Color = Color.Transparent, val unSelectedTabTextColor: Color = Color.White, + val unSelectedTabIconColor: Color = Color.White, + val borderColor: Color = Color.White.copy(alpha = .4f), ) -@ShowkaseComposable(name = "SegmentedTabBar", group = "TabBar", styleName = "Compact", defaultStyle = true) -@Preview -@Composable -fun SegmentedTabBarCompactPreview() { - AppThemeWithBackground(Theme.ThemeType.DARK) { - SegmentedTabBar( - items = listOf(stringResource(LR.string.plus_yearly), stringResource(LR.string.plus_monthly)), - onItemSelected = {}, - modifier = Modifier.width(IntrinsicSize.Max), - ) - } -} - @ShowkaseComposable(name = "SegmentedTabBar", group = "TabBar") @Preview @Composable -fun SegmentedTabBarFillWidthPreview() { +fun SegmentedTabBarPreview() { AppThemeWithBackground(Theme.ThemeType.DARK) { SegmentedTabBar( items = listOf(stringResource(LR.string.plus_yearly), stringResource(LR.string.plus_monthly)), onItemSelected = {}, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.width(IntrinsicSize.Max), ) } }