mirror of
https://github.com/bitwarden/android.git
synced 2026-11-09 10:34:18 -06:00
PM-45133: chore: Remove Premium Upgrade feature flag (#7544)
This commit is contained in:
+38
-61
@@ -5,7 +5,6 @@ import com.bitwarden.core.data.manager.model.FlagKey
|
||||
import com.bitwarden.core.data.repository.model.DataState
|
||||
import com.bitwarden.data.repository.model.Environment
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
import com.x8bit.bitwarden.data.auth.repository.util.activeUserIdChangesFlow
|
||||
import com.x8bit.bitwarden.data.billing.model.PremiumCard
|
||||
@@ -52,7 +51,7 @@ class PremiumStateManagerImpl(
|
||||
private val billingRepository: BillingRepository,
|
||||
private val settingsDiskSource: SettingsDiskSource,
|
||||
vaultRepository: VaultRepository,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
featureFlagManager: FeatureFlagManager,
|
||||
environmentRepository: EnvironmentRepository,
|
||||
pushManager: PushManager,
|
||||
private val clock: Clock,
|
||||
@@ -130,7 +129,7 @@ class PremiumStateManagerImpl(
|
||||
combine(
|
||||
authDiskSource.userStateFlow.map { it?.activeAccount },
|
||||
billingRepository.isInAppBillingSupportedFlow,
|
||||
featureFlagManager.getFeatureFlagFlow(FlagKey.MobilePremiumUpgrade),
|
||||
upgradeLifecycleStateFlow,
|
||||
authDiskSource.activeUserIdChangesFlow.flatMapLatest { userId ->
|
||||
userId
|
||||
?.let { id ->
|
||||
@@ -144,47 +143,35 @@ class PremiumStateManagerImpl(
|
||||
) {
|
||||
account,
|
||||
isInAppBillingSupported,
|
||||
featureFlagEnabled,
|
||||
lifecycle,
|
||||
isUpgradeCardDismissed,
|
||||
vaultDataState,
|
||||
->
|
||||
BannerInputs(
|
||||
account = account,
|
||||
isInAppBillingSupported = isInAppBillingSupported,
|
||||
featureFlagEnabled = featureFlagEnabled,
|
||||
isUpgradeCardDismissed = isUpgradeCardDismissed,
|
||||
vaultDataState = vaultDataState,
|
||||
)
|
||||
}
|
||||
.combine(upgradeLifecycleStateFlow) { inputs, lifecycle ->
|
||||
val profile = inputs.account?.profile ?: return@combine PremiumCard.NONE
|
||||
if (!inputs.featureFlagEnabled) return@combine PremiumCard.NONE
|
||||
val initialCard = when (lifecycle) {
|
||||
UpgradeLifecycleState.Free -> PremiumCard.UPGRADE
|
||||
UpgradeLifecycleState.UpgradePending -> PremiumCard.NONE
|
||||
is UpgradeLifecycleState.Premium -> {
|
||||
lifecycle.subscriptionStatus.premiumCardState()
|
||||
}
|
||||
}
|
||||
when (initialCard) {
|
||||
PremiumCard.UPGRADE -> {
|
||||
val isAccountOldEnough = profile.creationDate.isOlderThanDays(
|
||||
days = PREMIUM_UPGRADE_MINIMUM_ACCOUNT_AGE_DAYS,
|
||||
clock = clock,
|
||||
)
|
||||
val itemCount = inputs.vaultDataState.activeVaultItemCount()
|
||||
val showCard = inputs.isInAppBillingSupported &&
|
||||
isAccountOldEnough &&
|
||||
itemCount >= PREMIUM_UPGRADE_MINIMUM_VAULT_ITEMS &&
|
||||
!inputs.isUpgradeCardDismissed
|
||||
initialCard.takeIf { showCard } ?: PremiumCard.NONE
|
||||
}
|
||||
|
||||
PremiumCard.NEEDS_ATTENTION,
|
||||
PremiumCard.NONE,
|
||||
-> initialCard
|
||||
}
|
||||
val profile = account?.profile ?: return@combine PremiumCard.NONE
|
||||
val initialCard = when (lifecycle) {
|
||||
UpgradeLifecycleState.Free -> PremiumCard.UPGRADE
|
||||
UpgradeLifecycleState.UpgradePending -> PremiumCard.NONE
|
||||
is UpgradeLifecycleState.Premium -> lifecycle.subscriptionStatus.premiumCardState()
|
||||
}
|
||||
when (initialCard) {
|
||||
PremiumCard.UPGRADE -> {
|
||||
val isAccountOldEnough = profile.creationDate.isOlderThanDays(
|
||||
days = PREMIUM_UPGRADE_MINIMUM_ACCOUNT_AGE_DAYS,
|
||||
clock = clock,
|
||||
)
|
||||
val itemCount = vaultDataState.activeVaultItemCount()
|
||||
val showCard = isInAppBillingSupported &&
|
||||
isAccountOldEnough &&
|
||||
itemCount >= PREMIUM_UPGRADE_MINIMUM_VAULT_ITEMS &&
|
||||
!isUpgradeCardDismissed
|
||||
initialCard.takeIf { showCard } ?: PremiumCard.NONE
|
||||
}
|
||||
|
||||
PremiumCard.NEEDS_ATTENTION,
|
||||
PremiumCard.NONE,
|
||||
-> initialCard
|
||||
}
|
||||
}
|
||||
.stateIn(
|
||||
scope = unconfinedScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
@@ -211,17 +198,16 @@ class PremiumStateManagerImpl(
|
||||
* personal subscription to manage.
|
||||
*/
|
||||
override val isPlanRowEligibleFlow: StateFlow<Boolean> =
|
||||
combine(
|
||||
authDiskSource.userStateFlow,
|
||||
featureFlagManager.getFeatureFlagFlow(FlagKey.MobilePremiumUpgrade),
|
||||
) { userState, featureFlagEnabled ->
|
||||
val profile = userState?.activeAccount?.profile ?: return@combine false
|
||||
val hasPremium = profile.hasPremiumPersonally == true ||
|
||||
profile.hasPremiumFromOrganization == true
|
||||
val isPremiumFromSelf = profile.hasPremiumPersonally == true
|
||||
val isOrgOnlyPremium = hasPremium && !isPremiumFromSelf
|
||||
featureFlagEnabled && !isOrgOnlyPremium
|
||||
}
|
||||
authDiskSource
|
||||
.userStateFlow
|
||||
.map { userState ->
|
||||
val profile = userState?.activeAccount?.profile ?: return@map false
|
||||
val hasPremium = profile.hasPremiumPersonally == true ||
|
||||
profile.hasPremiumFromOrganization == true
|
||||
val isPremiumFromSelf = profile.hasPremiumPersonally == true
|
||||
val isOrgOnlyPremium = hasPremium && !isPremiumFromSelf
|
||||
!isOrgOnlyPremium
|
||||
}
|
||||
.stateIn(
|
||||
scope = unconfinedScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
@@ -311,8 +297,7 @@ class PremiumStateManagerImpl(
|
||||
override val isSelfHosted: Boolean get() = isSelfHostedFlow.value
|
||||
|
||||
override fun isInAppUpgradeAvailable(): Boolean =
|
||||
billingRepository.isInAppBillingSupportedFlow.value &&
|
||||
featureFlagManager.getFeatureFlag(FlagKey.MobilePremiumUpgrade)
|
||||
billingRepository.isInAppBillingSupportedFlow.value
|
||||
|
||||
override fun dismissPremiumUpgradeBanner() {
|
||||
val activeUserId = authDiskSource.userState?.activeUserId ?: return
|
||||
@@ -406,14 +391,6 @@ class PremiumStateManagerImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private data class BannerInputs(
|
||||
val account: AccountJson?,
|
||||
val isInAppBillingSupported: Boolean,
|
||||
val featureFlagEnabled: Boolean,
|
||||
val isUpgradeCardDismissed: Boolean,
|
||||
val vaultDataState: DataState<VaultData>,
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns a [PremiumCard] for the given [SubscriptionStatusState] and subscription substate.
|
||||
*/
|
||||
|
||||
+7
-48
@@ -73,15 +73,8 @@ class PremiumStateManagerTest {
|
||||
every { vaultDataStateFlow } returns mutableVaultDataStateFlow
|
||||
}
|
||||
|
||||
private val mutableMobilePremiumUpgradeFlagFlow = MutableStateFlow(true)
|
||||
private val mutableDebugDisableSelfHostPremiumCheckFlagFlow = MutableStateFlow(false)
|
||||
private val featureFlagManager: FeatureFlagManager = mockk {
|
||||
every {
|
||||
getFeatureFlagFlow(FlagKey.MobilePremiumUpgrade)
|
||||
} returns mutableMobilePremiumUpgradeFlagFlow
|
||||
every {
|
||||
getFeatureFlag(FlagKey.MobilePremiumUpgrade)
|
||||
} answers { mutableMobilePremiumUpgradeFlagFlow.value }
|
||||
every {
|
||||
getFeatureFlag(FlagKey.DebugDisableSelfHostPremiumCheck)
|
||||
} answers { mutableDebugDisableSelfHostPremiumCheckFlagFlow.value }
|
||||
@@ -141,15 +134,6 @@ class PremiumStateManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ineligible when feature flag is disabled should emit NONE`() = runTest {
|
||||
mutableMobilePremiumUpgradeFlagFlow.value = false
|
||||
val manager = createManager()
|
||||
manager.premiumCardStateFlow.test {
|
||||
assertEquals(PremiumCard.NONE, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ineligible when banner is dismissed should emit NONE`() = runTest {
|
||||
fakeSettingsDiskSource.storePremiumUpgradeBannerDismissed(
|
||||
@@ -356,7 +340,7 @@ class PremiumStateManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isInAppUpgradeAvailable should return true when billing supported and flag enabled`() {
|
||||
fun `isInAppUpgradeAvailable should return true when billing is supported`() {
|
||||
val manager = createManager()
|
||||
assertTrue(manager.isInAppUpgradeAvailable())
|
||||
}
|
||||
@@ -368,21 +352,6 @@ class PremiumStateManagerTest {
|
||||
assertFalse(manager.isInAppUpgradeAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isInAppUpgradeAvailable should return false when feature flag disabled`() {
|
||||
mutableMobilePremiumUpgradeFlagFlow.value = false
|
||||
val manager = createManager()
|
||||
assertFalse(manager.isInAppUpgradeAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isInAppUpgradeAvailable should return false when both conditions are false`() {
|
||||
mutableIsInAppBillingSupportedFlow.value = false
|
||||
mutableMobilePremiumUpgradeFlagFlow.value = false
|
||||
val manager = createManager()
|
||||
assertFalse(manager.isInAppUpgradeAvailable())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isSelfHosted should return false on cloud environment regardless of flag`() {
|
||||
fakeEnvironmentRepository.environment = Environment.Prod.Us
|
||||
@@ -474,14 +443,13 @@ class PremiumStateManagerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isPlanRowEligibleFlow emits true for a free user when feature flag is enabled`() =
|
||||
runTest {
|
||||
fakeAuthDiskSource.userState = userStateJsonWith(account = createAccountJson())
|
||||
val manager = createManager()
|
||||
manager.isPlanRowEligibleFlow.test {
|
||||
assertTrue(awaitItem())
|
||||
}
|
||||
fun `isPlanRowEligibleFlow emits true when user is a free user`() = runTest {
|
||||
fakeAuthDiskSource.userState = userStateJsonWith(account = createAccountJson())
|
||||
val manager = createManager()
|
||||
manager.isPlanRowEligibleFlow.test {
|
||||
assertTrue(awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isPlanRowEligibleFlow emits true for a personal Premium user`() = runTest {
|
||||
@@ -506,15 +474,6 @@ class PremiumStateManagerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isPlanRowEligibleFlow emits false when feature flag is disabled`() = runTest {
|
||||
mutableMobilePremiumUpgradeFlagFlow.value = false
|
||||
val manager = createManager()
|
||||
manager.isPlanRowEligibleFlow.test {
|
||||
assertFalse(awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isPlanRowEligibleFlow emits false when userState is null`() = runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
@@ -34,7 +34,6 @@ sealed class FlagKey<out T : Any> {
|
||||
NoLogoutOnKdfChange,
|
||||
MigrateMyVaultToMyItems,
|
||||
CardScanner,
|
||||
MobilePremiumUpgrade,
|
||||
ManageDevices,
|
||||
AttachmentUpdates,
|
||||
V2EncryptionJitPassword,
|
||||
@@ -95,14 +94,6 @@ sealed class FlagKey<out T : Any> {
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key for the mobile Premium upgrade feature.
|
||||
*/
|
||||
data object MobilePremiumUpgrade : FlagKey<Boolean>() {
|
||||
override val keyName: String = "pm-31697-premium-upgrade-path"
|
||||
override val defaultValue: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Data object holding the feature flag key for the Attachment Feature Updates.
|
||||
*/
|
||||
|
||||
@@ -20,10 +20,6 @@ class FlagKeyTest {
|
||||
FlagKey.MigrateMyVaultToMyItems.keyName,
|
||||
"pm-20558-migrate-myvault-to-myitems",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.MobilePremiumUpgrade.keyName,
|
||||
"pm-31697-premium-upgrade-path",
|
||||
)
|
||||
assertEquals(
|
||||
FlagKey.AttachmentUpdates.keyName,
|
||||
"pm-34224-mobile-attachment-updates",
|
||||
@@ -93,7 +89,6 @@ class FlagKeyTest {
|
||||
FlagKey.BitwardenAuthenticationEnabled,
|
||||
FlagKey.ForceUpdateKdfSettings,
|
||||
FlagKey.MigrateMyVaultToMyItems,
|
||||
FlagKey.MobilePremiumUpgrade,
|
||||
FlagKey.AttachmentUpdates,
|
||||
FlagKey.V2EncryptionJitPassword,
|
||||
FlagKey.V2EncryptionKeyConnector,
|
||||
|
||||
@@ -28,7 +28,6 @@ fun <T : Any> FlagKey<T>.ListItemContent(
|
||||
FlagKey.NoLogoutOnKdfChange,
|
||||
FlagKey.MigrateMyVaultToMyItems,
|
||||
FlagKey.CardScanner,
|
||||
FlagKey.MobilePremiumUpgrade,
|
||||
FlagKey.ManageDevices,
|
||||
FlagKey.AttachmentUpdates,
|
||||
FlagKey.V2EncryptionJitPassword,
|
||||
@@ -94,7 +93,6 @@ private fun <T : Any> FlagKey<T>.getDisplayLabel(): String = when (this) {
|
||||
|
||||
FlagKey.MigrateMyVaultToMyItems -> stringResource(BitwardenString.migrate_my_vault_to_my_items)
|
||||
FlagKey.CardScanner -> stringResource(BitwardenString.scan_card)
|
||||
FlagKey.MobilePremiumUpgrade -> stringResource(BitwardenString.mobile_premium_upgrade)
|
||||
FlagKey.ManageDevices -> stringResource(BitwardenString.manage_devices)
|
||||
FlagKey.AttachmentUpdates -> stringResource(BitwardenString.attachment_updates)
|
||||
FlagKey.V2EncryptionJitPassword -> stringResource(BitwardenString.v2_encryption_jit_password)
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
<string name="migrate_my_vault_to_my_items">Migrate My Vault to My Items</string>
|
||||
<string name="trigger_cookie_acquisition">Trigger cookie acquisition</string>
|
||||
<string name="clear_sso_cookies">Clear SSO cookies</string>
|
||||
<string name="mobile_premium_upgrade">Mobile Premium Upgrade</string>
|
||||
<string name="attachment_updates">Attachment Updates</string>
|
||||
<string name="v2_encryption_tde">V2 Encryption - TDE</string>
|
||||
<string name="v2_encryption_key_connector">V2 Encryption - Key Connector</string>
|
||||
|
||||
Reference in New Issue
Block a user