Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patron - Add icons #953

Merged
merged 11 commits into from
May 12, 2023
48 changes: 48 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,54 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".ui.MainActivity_14"
android:label="@string/app_name"
android:targetActivity=".ui.MainActivity"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_patron_chrome">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".ui.MainActivity_15"
android:label="@string/app_name"
android:targetActivity=".ui.MainActivity"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_patron_round">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".ui.MainActivity_16"
android:label="@string/app_name"
android:targetActivity=".ui.MainActivity"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_patron_glow">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".ui.MainActivity_17"
android:label="@string/app_name"
android:targetActivity=".ui.MainActivity"
android:enabled="false"
android:exported="true"
android:icon="@mipmap/ic_launcher_patron_dark">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>

<activity android:name=".profile.sonos.SonosAppLinkActivity" android:label="@string/profile_sonos"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ private fun Content(
is OnboardingFlow.PlusFlow -> defaultValue = flow.source
else -> Unit // Not a startDestination, default value should not be set.
}
},
navArgument(OnboardingNavRoute.PlusUpgrade.showPatronOnlyArgumentKey) {
type = NavType.BoolType
defaultValue = when (flow) {
is OnboardingFlow.PlusUpsell -> flow.showPatronOnly
else -> false
}
}
)
) { navBackStackEntry ->
Expand Down Expand Up @@ -258,9 +265,13 @@ private object OnboardingNavRoute {
private const val routeBase = "plus_upgrade"

const val sourceArgumentKey = "source"
const val showPatronOnlyArgumentKey = "show_patron_only"
// The route variable should only be used to navigate to the PlusUpgrade screens
// when they are the startDestination. In all other cases, use the routeWithSource function.
const val route = "$routeBase/{$sourceArgumentKey}"
// when they are the startDestination and the args for these startDestinations are set using default values.
// They are parsed based on this deep-link-like route by the navigation component.
// For more details check here: https://developer.android.com/jetpack/compose/navigation#nav-with-args
// In all other cases, use the routeWithSource function.
const val route = "$routeBase/{$sourceArgumentKey}?{$showPatronOnlyArgumentKey}={$showPatronOnlyArgumentKey}"
fun routeWithSource(source: OnboardingUpgradeSource) = "$routeBase/$source"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class OnboardingUpgradeFeaturesViewModel @Inject constructor(
val state: StateFlow<OnboardingUpgradeFeaturesState> = _state

private val source = savedStateHandle.get<OnboardingUpgradeSource>("source")
private val showPatronOnly = savedStateHandle.get<Boolean>("show_patron_only")
Comment on lines 52 to +53
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of hardcoding these, I think it would be good to reuse the variables from OnboardingNavRoute. In addition to ensuring they stay consistent, this also makes it easier to use code navigation to see where this argument is being set and used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already tried but there's a cyclic dependency between the account and profile modules 😞


init {
if (BuildConfig.ADD_PATRON_ENABLED) {
Expand Down Expand Up @@ -89,7 +90,7 @@ class OnboardingUpgradeFeaturesViewModel @Inject constructor(
val lastSelectedTier = settings.getLastSelectedSubscriptionTier().takeIf { source in listOf(OnboardingUpgradeSource.LOGIN, OnboardingUpgradeSource.PROFILE) }
val lastSelectedFrequency = settings.getLastSelectedSubscriptionFrequency().takeIf { source in listOf(OnboardingUpgradeSource.LOGIN, OnboardingUpgradeSource.PROFILE) }

val showPatronOnly = source == OnboardingUpgradeSource.ACCOUNT_DETAILS
val showPatronOnly = source == OnboardingUpgradeSource.ACCOUNT_DETAILS || showPatronOnly == true
val updatedSubscriptions =
if (showPatronOnly) {
subscriptions.filter { it.tier == Subscription.SubscriptionTier.PATRON }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import au.com.shiftyjelly.pocketcasts.models.to.SignInState
import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionType
import au.com.shiftyjelly.pocketcasts.settings.databinding.AdapterAppearanceAppiconItemBinding
import au.com.shiftyjelly.pocketcasts.ui.extensions.getThemeColor
import au.com.shiftyjelly.pocketcasts.ui.helper.AppIcon
Expand All @@ -18,7 +20,7 @@ import au.com.shiftyjelly.pocketcasts.ui.R as UR

class AppearanceIconSettingsAdapter(
private var mainWidth: Int?,
private var isPlusSignedIn: Boolean,
private var signInState: SignInState?,
private var selectedAppIcon: AppIcon.AppIconType,
private val list: List<AppIcon.AppIconType>,
private val clickListener: (AppIcon.AppIconType, AppIcon.AppIconType, Boolean) -> Unit
Expand Down Expand Up @@ -50,8 +52,8 @@ class AppearanceIconSettingsAdapter(

override fun getItemCount() = list.size

fun updatePlusSignedIn(value: Boolean) {
isPlusSignedIn = value
fun updatePlusSignedIn(signInState: SignInState?) {
this.signInState = signInState
notifyDataSetChanged()
}

Expand Down Expand Up @@ -82,20 +84,25 @@ class AppearanceIconSettingsAdapter(
}

fun bind(appIcon: AppIcon.AppIconType, selected: Boolean) {
val showOption = !appIcon.isPlus || isPlusSignedIn
binding.appIconItem.alpha = if (showOption) 1.0f else 0.65f
val isValidIcon = isValidIcon(appIcon)
binding.appIconItem.alpha = if (isValidIcon) 1.0f else 0.65f

val drawable = AppCompatResources.getDrawable(itemView.context, appIcon.settingsIcon)
binding.imgIcon.setImageDrawable(drawable)
var tickDrawable: Drawable? = null
if (showOption && selected) {
if (isValidIcon && selected) {
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_circle_tick)
} else if (!showOption) {
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_plus_bubble)
} else if (!isValidIcon) {
val iconDrawable = when (appIcon.type) {
SubscriptionType.PLUS -> R.drawable.ic_locked_plus
SubscriptionType.PATRON -> R.drawable.ic_locked_patron
SubscriptionType.NONE -> throw IllegalStateException("Unknown type found for AppIcon")
}
tickDrawable = AppCompatResources.getDrawable(itemView.context, iconDrawable)
}
binding.imgTick.setImageDrawable(tickDrawable)
binding.imgTick.contentDescription = binding.imgTick.resources.getString(if (selected) LR.string.on else LR.string.off)
binding.imgLock.isVisible = !showOption
binding.imgLock.isVisible = !isValidIcon
binding.txtTitle.setText(appIcon.labelId)

binding.outlinePanel1.setSelectedWithColors(selected, selectColor, deselectColor, strokeWidth)
Expand All @@ -107,11 +114,16 @@ class AppearanceIconSettingsAdapter(
}
val beforeAppIcon = selectedAppIcon
val afterAppIcon = list[bindingAdapterPosition]
val validAppIcon = !afterAppIcon.isPlus || isPlusSignedIn
val validAppIcon = isValidIcon(afterAppIcon)

selectedAppIcon = afterAppIcon
clickListener(beforeAppIcon, afterAppIcon, validAppIcon)
notifyDataSetChanged()
}

private fun isValidIcon(appIcon: AppIcon.AppIconType) =
(appIcon.type == SubscriptionType.NONE) ||
(appIcon.type == SubscriptionType.PLUS && signInState?.isSignedInAsPlus == true) ||
(appIcon.type == SubscriptionType.PATRON && signInState?.isSignedInAsPatron == true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import au.com.shiftyjelly.pocketcasts.models.type.SubscriptionType
import au.com.shiftyjelly.pocketcasts.preferences.Settings
import au.com.shiftyjelly.pocketcasts.repositories.subscription.SubscriptionManager
import au.com.shiftyjelly.pocketcasts.settings.databinding.FragmentSettingsAppearanceBinding
Expand Down Expand Up @@ -85,7 +86,7 @@ class AppearanceSettingsFragment : BaseFragment() {
binding.themeRecyclerView.setHasFixedSize(true)
scrollToCurrentTheme()

binding.appIconRecyclerView.adapter = AppearanceIconSettingsAdapter(mainWidth, isSignedInAsPlus, state.currentAppIcon, state.iconList) { beforeAppIconType, afterAppIconType, validIcon ->
binding.appIconRecyclerView.adapter = AppearanceIconSettingsAdapter(mainWidth, viewModel.signInState.value, state.currentAppIcon, state.iconList) { beforeAppIconType, afterAppIconType, validIcon ->
if (validIcon) {
viewModel.updateGlobalIcon(afterAppIconType)

Expand All @@ -96,7 +97,7 @@ class AppearanceSettingsFragment : BaseFragment() {
.show()
} else {
viewModel.updateChangeAppIconType(Pair(beforeAppIconType, afterAppIconType))
openOnboardingFlow()
openOnboardingFlow(afterAppIconType.type)
}
}
binding.appIconRecyclerView.setHasFixedSize(true)
Expand Down Expand Up @@ -148,7 +149,7 @@ class AppearanceSettingsFragment : BaseFragment() {
}

(binding.themeRecyclerView.adapter as? AppearanceThemeSettingsAdapter)?.updatePlusSignedIn(signInState.isSignedInAsPlus)
(binding.appIconRecyclerView.adapter as? AppearanceIconSettingsAdapter)?.updatePlusSignedIn(signInState.isSignedInAsPlus)
(binding.appIconRecyclerView.adapter as? AppearanceIconSettingsAdapter)?.updatePlusSignedIn(signInState)
binding.upgradeGroup.isVisible = !signInState.isSignedInAsPlus && !settings.getUpgradeClosedAppearSettings()
}

Expand Down Expand Up @@ -191,8 +192,11 @@ class AppearanceSettingsFragment : BaseFragment() {
viewModel.onShown()
}

private fun openOnboardingFlow() {
OnboardingLauncher.openOnboardingFlow(activity, OnboardingFlow.PlusUpsell(OnboardingUpgradeSource.APPEARANCE))
private fun openOnboardingFlow(type: SubscriptionType? = null) {
val onboardingFlow = type?.takeIf { type == SubscriptionType.PATRON }?.let {
OnboardingFlow.PlusUpsell(source = OnboardingUpgradeSource.APPEARANCE, showPatronOnly = true)
} ?: OnboardingFlow.PlusUpsell(OnboardingUpgradeSource.APPEARANCE)
OnboardingLauncher.openOnboardingFlow(activity, onboardingFlow)
}

private fun scrollToCurrentTheme() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class AppearanceThemeSettingsAdapter(
if (showOption && selected) {
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_circle_tick)
} else if (!showOption) {
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_plus_bubble)
tickDrawable = AppCompatResources.getDrawable(itemView.context, R.drawable.ic_locked_plus)
}
binding.imgTick.setImageDrawable(tickDrawable)
binding.imgTick.contentDescription = binding.imgTick.resources.getString(if (selected) LR.string.on else LR.string.off)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ sealed class OnboardingFlow(val analyticsValue: String) : Parcelable {
@Parcelize object InitialOnboarding : OnboardingFlow("initial_onboarding")
@Parcelize class PlusAccountUpgrade(override val source: OnboardingUpgradeSource) : PlusFlow, OnboardingFlow("plus_account_upgrade")
@Parcelize object PlusAccountUpgradeNeedsLogin : OnboardingFlow("plus_account_upgrade_needs_login")
@Parcelize class PlusUpsell(override val source: OnboardingUpgradeSource) : PlusFlow, OnboardingFlow("plus_upsell")
@Parcelize class PlusUpsell(
override val source: OnboardingUpgradeSource,
val showPatronOnly: Boolean = false,
) : PlusFlow, OnboardingFlow(if (showPatronOnly) "patron_upsell" else "plus_upsell")
@Parcelize class PatronAccountUpgrade(override val source: OnboardingUpgradeSource) : PlusFlow, OnboardingFlow("patron_account_upgrade")

sealed interface PlusFlow {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.toLiveData
import au.com.shiftyjelly.pocketcasts.analytics.AnalyticsEvent
import au.com.shiftyjelly.pocketcasts.analytics.AnalyticsTrackerWrapper
import au.com.shiftyjelly.pocketcasts.analytics.BuildConfig
import au.com.shiftyjelly.pocketcasts.models.to.SignInState
import au.com.shiftyjelly.pocketcasts.preferences.Settings
import au.com.shiftyjelly.pocketcasts.repositories.podcast.UserEpisodeManager
Expand Down Expand Up @@ -64,11 +65,22 @@ class SettingsAppearanceViewModel @Inject constructor(

fun loadThemesAndIcons() {
createAccountState.postValue(SettingsAppearanceState.ThemesAndIconsLoading)

val appIcons = if (BuildConfig.ADD_PATRON_ENABLED) {
appIcon.allAppIconTypes.toList()
} else {
appIcon.allAppIconTypes.toList().filterNot {
it in listOf(
AppIcon.AppIconType.PATRON_CHROME,
AppIcon.AppIconType.PATRON_ROUND,
AppIcon.AppIconType.PATRON_GLOW,
AppIcon.AppIconType.PATRON_DARK,
)
}
}
createAccountState.postValue(
SettingsAppearanceState.ThemesAndIconsLoaded(
theme.activeTheme, theme.allThemes.toList(),
appIcon.activeAppIcon, appIcon.allAppIconTypes.toList()
appIcon.activeAppIcon, appIcons
)
)
}
Expand All @@ -94,6 +106,10 @@ class SettingsAppearanceViewModel @Inject constructor(
AppIcon.AppIconType.ELECTRIC_PINK -> "electric_pink"
AppIcon.AppIconType.RADIOACTIVE -> "radioactive"
AppIcon.AppIconType.HALLOWEEN -> "halloween"
AppIcon.AppIconType.PATRON_CHROME -> "patron_chrome"
AppIcon.AppIconType.PATRON_ROUND -> "patron_round"
AppIcon.AppIconType.PATRON_GLOW -> "patron_glow"
AppIcon.AppIconType.PATRON_DARK -> "patron_dark"
}
)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<vector android:height="24dp" android:viewportHeight="30"
android:viewportWidth="30" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#03A9F4" android:pathData="M15,28.75C7.4061,28.75 1.25,22.5939 1.25,15C1.25,7.4061 7.4061,1.25 15,1.25C22.5939,1.25 28.75,7.4061 28.75,15C28.75,22.5939 22.5939,28.75 15,28.75Z"/>
<path android:fillColor="#ffffff" android:pathData="M19.0458,10.4426C19.4917,9.9156 20.2804,9.8499 20.8074,10.2958C21.3344,10.7417 21.4002,11.5304 20.9542,12.0575L13.2017,21.2195L8.4911,16.5089C8.0029,16.0208 8.0029,15.2293 8.4911,14.7411C8.9793,14.253 9.7707,14.253 10.2589,14.7411L13.0483,17.5305L19.0458,10.4426Z"/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="40dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M20,33.75C12.406,33.75 6.25,27.594 6.25,20C6.25,12.406 12.406,6.25 20,6.25C27.594,6.25 33.75,12.406 33.75,20C33.75,27.594 27.594,33.75 20,33.75Z"
android:fillColor="#03A9F4"/>
<path
android:pathData="M24.046,15.443C24.492,14.916 25.28,14.85 25.807,15.296C26.334,15.742 26.4,16.53 25.954,17.058L18.202,26.219L13.491,21.509C13.003,21.021 13.003,20.229 13.491,19.741C13.979,19.253 14.771,19.253 15.259,19.741L18.048,22.531L24.046,15.443Z"
android:fillColor="#ffffff"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="40dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M20.117,20.117m-13.749,-0.118a13.75,13.75 124.03,1 1,27.499 0.235a13.75,13.75 124.03,1 1,-27.499 -0.235"
android:fillColor="#6046F5"/>
<path
android:pathData="M16.75,18.031V17.5C16.75,15.705 18.205,14.25 20,14.25C21.795,14.25 23.25,15.705 23.25,17.5V18.031C23.681,18.142 24,18.534 24,19V24C24,24.552 23.552,25 23,25H17C16.448,25 16,24.552 16,24V19C16,18.534 16.319,18.142 16.75,18.031ZM18.25,17.5C18.25,16.534 19.034,15.75 20,15.75C20.966,15.75 21.75,16.534 21.75,17.5V18H18.25V17.5Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="40dp"
android:height="40dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M20.117,20.117m-13.749,-0.118a13.75,13.75 124.03,1 1,27.499 0.235a13.75,13.75 124.03,1 1,-27.499 -0.235">
<aapt:attr name="android:fillColor">
<gradient
android:startX="8.941"
android:startY="6.271"
android:endX="33.794"
android:endY="8.635"
android:type="linear">
<item android:offset="0" android:color="#FFFED745"/>
<item android:offset="1" android:color="#FFFEB525"/>
</gradient>
</aapt:attr>
</path>
<path
android:pathData="M16.75,18.031V17.5C16.75,15.705 18.205,14.25 20,14.25C21.795,14.25 23.25,15.705 23.25,17.5V18.031C23.681,18.142 24,18.534 24,19V24C24,24.552 23.552,25 23,25H17C16.448,25 16,24.552 16,24V19C16,18.534 16.319,18.142 16.75,18.031ZM18.25,17.5C18.25,16.534 19.034,15.75 20,15.75C20.966,15.75 21.75,16.534 21.75,17.5V18H18.25V17.5Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>
Loading