@@ -76,6 +76,7 @@ import com.duckduckgo.subscriptions.impl.services.ValidateTokenResponse
7676import com.duckduckgo.subscriptions.impl.services.toEntitlements
7777import com.duckduckgo.subscriptions.impl.wideevents.AuthTokenRefreshWideEvent
7878import com.duckduckgo.subscriptions.impl.wideevents.SubscriptionPurchaseWideEvent
79+ import com.duckduckgo.subscriptions.impl.wideevents.SubscriptionSwitchWideEvent
7980import com.squareup.anvil.annotations.ContributesBinding
8081import com.squareup.moshi.JsonDataException
8182import com.squareup.moshi.JsonEncodingException
@@ -295,6 +296,7 @@ class RealSubscriptionsManager @Inject constructor(
295296 private val backgroundTokenRefresh : BackgroundTokenRefresh ,
296297 private val subscriptionPurchaseWideEvent : SubscriptionPurchaseWideEvent ,
297298 private val tokenRefreshWideEvent : AuthTokenRefreshWideEvent ,
299+ private val subscriptionSwitchWideEvent : SubscriptionSwitchWideEvent ,
298300) : SubscriptionsManager {
299301 private val adapter = Moshi .Builder ().build().adapter(ResponseError ::class .java)
300302
@@ -371,10 +373,12 @@ class RealSubscriptionsManager @Inject constructor(
371373 when (it) {
372374 is PurchaseState .Purchased -> {
373375 subscriptionPurchaseWideEvent.onBillingFlowPurchaseSuccess()
376+ subscriptionSwitchWideEvent.onPlayBillingSwitchSuccess()
374377 checkPurchase(it.packageName, it.purchaseToken)
375378 }
376379 is PurchaseState .Canceled -> {
377380 _currentPurchaseState .emit(CurrentPurchase .Canceled )
381+ subscriptionSwitchWideEvent.onUserCancelled()
378382 if (removeExpiredSubscriptionOnCancelledPurchase) {
379383 if (subscriptionStatus().isExpired()) {
380384 signOut()
@@ -391,6 +395,7 @@ class RealSubscriptionsManager @Inject constructor(
391395 }
392396 }
393397
398+
394399 override suspend fun canSupportEncryption (): Boolean = authRepository.canSupportEncryption()
395400
396401 override suspend fun isFreeTrialEligible (): Boolean {
@@ -499,21 +504,34 @@ class RealSubscriptionsManager @Inject constructor(
499504 try {
500505 if (! isSignedIn()) {
501506 logcat { " Subs: Cannot switch plan - user not signed in" }
507+ subscriptionSwitchWideEvent.onValidationFailure(" User not signed in" )
502508 _currentPurchaseState .emit(CurrentPurchase .Failure (" User not signed in for switch" ))
503509 return @withContext
504510 }
505511
506512 val currentSubscription = authRepository.getSubscription()
507513 if (currentSubscription == null || ! currentSubscription.isActive()) {
508- logcat { " Subs: Cannot switch plan - no active subscription found" }
514+ subscriptionSwitchWideEvent.onValidationFailure( " No active subscription found" )
509515 _currentPurchaseState .emit(CurrentPurchase .Failure (" No active subscription found for switch" ))
510516 return @withContext
511517 }
512518
519+ // Start wide event tracking
520+ val isUpgrade = currentSubscription.productId in listOf (MONTHLY_PLAN_US , MONTHLY_PLAN_ROW )
521+ val switchType = if (isUpgrade) " upgrade" else " downgrade"
522+ subscriptionSwitchWideEvent.onSwitchFlowStarted(
523+ fromPlan = currentSubscription.productId,
524+ toPlan = planId,
525+ switchType = switchType,
526+ )
527+
528+ subscriptionSwitchWideEvent.onCurrentSubscriptionValidated()
529+
513530 val currentPurchaseToken = playBillingManager.getLatestPurchaseToken()
514531
515532 if (currentPurchaseToken == null ) {
516533 logcat { " Subs: Cannot switch plan - no current purchase token found" }
534+ subscriptionSwitchWideEvent.onSwitchFailed(" No current purchase token found" )
517535 _currentPurchaseState .emit(CurrentPurchase .Failure (" No current purchase token found for switch" ))
518536 return @withContext
519537 }
@@ -522,6 +540,7 @@ class RealSubscriptionsManager @Inject constructor(
522540 val account = authRepository.getAccount()
523541 if (account == null ) {
524542 logcat { " Subs: Cannot switch plan - no account found" }
543+ subscriptionSwitchWideEvent.onSwitchFailed(" No account found" )
525544 _currentPurchaseState .emit(CurrentPurchase .Failure (" No account found for switch" ))
526545 return @withContext
527546 }
@@ -531,10 +550,13 @@ class RealSubscriptionsManager @Inject constructor(
531550 val targetOffer = availableOffers.find { it.planId == planId && it.offerId == offerId }
532551 if (targetOffer == null ) {
533552 logcat { " Subs: Cannot switch plan - target plan not found: $planId " }
553+ subscriptionSwitchWideEvent.onTargetPlanRetrievalFailure()
534554 _currentPurchaseState .emit(CurrentPurchase .Failure (" Target plan not found: $planId for switch" ))
535555 return @withContext
536556 }
537557
558+ subscriptionSwitchWideEvent.onTargetPlanRetrieved()
559+
538560 // Launch Google Play billing flow for subscription update
539561 logcat { " Subs: Launching subscription update flow for plan: $planId " }
540562
@@ -549,8 +571,11 @@ class RealSubscriptionsManager @Inject constructor(
549571 replacementMode = replacementMode,
550572 )
551573 }
574+
575+ subscriptionSwitchWideEvent.onBillingFlowInitSuccess()
552576 } catch (e: Exception ) {
553577 logcat(ERROR ) { " Subs: Failed to switch subscription plan: ${e.asLog()} " }
578+ subscriptionSwitchWideEvent.onBillingFlowInitFailure(e.message ? : " Unknown error" )
554579 _currentPurchaseState .emit(CurrentPurchase .Failure (" Failed to switch subscription plan: ${e.message} " ))
555580 }
556581 }
@@ -693,8 +718,11 @@ class RealSubscriptionsManager @Inject constructor(
693718 emitEntitlementsValues()
694719 _currentPurchaseState .emit(CurrentPurchase .Success )
695720 authRepository.registerLocalPurchasedAt()
721+
722+ subscriptionSwitchWideEvent.onSwitchConfirmed()
696723 subscriptionPurchaseWideEvent.onPurchaseConfirmationSuccess()
697724 } else {
725+ subscriptionSwitchWideEvent.onSwitchConfirmationFailure(" Subscription not active after switch" )
698726 handlePurchaseFailed()
699727 }
700728
@@ -890,6 +918,7 @@ class RealSubscriptionsManager @Inject constructor(
890918 )
891919
892920 subscriptionPurchaseWideEvent.onSubscriptionUpdated(oldStatus = oldStatus, newStatus = subscription.status.toStatus())
921+ subscriptionSwitchWideEvent.onSubscriptionUpdated(oldStatus = oldStatus, newStatus = subscription.status.toStatus())
893922
894923 _subscriptionStatus .emit(subscription.status.toStatus())
895924 }
@@ -1390,7 +1419,6 @@ data class PricingPhase(
13901419 val priceCurrency : Currency ,
13911420 val formattedPrice : String ,
13921421 val billingPeriod : String ,
1393-
13941422) {
13951423 internal fun getBillingPeriodInDays (): Int? {
13961424 return try {
0 commit comments