Phase 6: Add Android subscription UI with Jetpack Compose

Implemented Android subscription UI components:
- UpgradeFeatureScreen: Full-screen view for restricted features (contractors, documents)
  * Shows feature icon, name, and description
  * Displays "This feature is available with Pro" badge
  * Opens UpgradePromptDialog on button click
- UpgradePromptDialog: Modal dialog with upgrade options
  * Trigger-specific title and message from backend
  * Feature preview list with Material icons
  * "Upgrade to Pro" button with loading state
  * "Compare Free vs Pro" button
  * "Maybe Later" cancel option
- FeatureComparisonDialog: Full-screen comparison table
  * Free vs Pro feature comparison
  * Displays data from SubscriptionCache.featureBenefits
  * Default features if no data loaded
  * Upgrade button
- BillingManager: Google Play Billing Library placeholder
  * Singleton manager for in-app purchases
  * Product query placeholder
  * Purchase flow placeholder
  * Backend receipt verification placeholder
  * Restore purchases placeholder

All components follow Material3 design system with theme-aware colors and spacing constants.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-24 13:41:06 -06:00
parent d12a2d315c
commit bf2e572abf
4 changed files with 617 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
package com.example.mycrib.platform
import android.app.Activity
import android.content.Context
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
/**
* Google Play Billing manager for in-app purchases
* NOTE: Requires Google Play Console configuration and product IDs
*/
class BillingManager private constructor(context: Context) {
companion object {
@Volatile
private var INSTANCE: BillingManager? = null
fun getInstance(context: Context): BillingManager {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: BillingManager(context.applicationContext).also { INSTANCE = it }
}
}
}
// Product ID for Pro subscription (configure in Google Play Console)
private val proSubscriptionProductID = "com.example.mycrib.pro.monthly"
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading
private val _purchasedProductIDs = MutableStateFlow<Set<String>>(emptySet())
val purchasedProductIDs: StateFlow<Set<String>> = _purchasedProductIDs
init {
// Start listening for purchases
// In production, initialize BillingClient here
println("BillingManager: Initialized")
}
/**
* Connect to Google Play Billing
*/
fun startConnection(onSuccess: () -> Unit, onError: (String) -> Unit) {
// In production, this would connect to BillingClient
// billingClient.startConnection(object : BillingClientStateListener {
// override fun onBillingSetupFinished(billingResult: BillingResult) {
// if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
// onSuccess()
// } else {
// onError("Billing setup failed")
// }
// }
// override fun onBillingServiceDisconnected() { /* Retry */ }
// })
println("BillingManager: Would connect to Google Play Billing")
onSuccess()
}
/**
* Query available products
*/
suspend fun queryProducts(): List<ProductDetails> {
_isLoading.value = true
try {
// In production, this would query real products
// val params = QueryProductDetailsParams.newBuilder()
// .setProductList(listOf(
// Product.newBuilder()
// .setProductId(proSubscriptionProductID)
// .setProductType(BillingClient.ProductType.SUBS)
// .build()
// ))
// .build()
// val result = billingClient.queryProductDetails(params)
println("BillingManager: Would query products here")
return emptyList()
} finally {
_isLoading.value = false
}
}
/**
* Launch purchase flow
*/
fun launchPurchaseFlow(activity: Activity, productId: String, onSuccess: () -> Unit, onError: (String) -> Unit) {
// In production, this would launch the purchase UI
// val params = BillingFlowParams.newBuilder()
// .setProductDetailsParamsList(listOf(productDetailsParams))
// .build()
// val billingResult = billingClient.launchBillingFlow(activity, params)
println("BillingManager: Would launch purchase flow for: $productId")
onError("Purchase not implemented yet")
}
/**
* Verify purchase with backend
*/
suspend fun verifyPurchaseWithBackend(purchaseToken: String, productId: String) {
// TODO: Call backend API to verify purchase
// val api = SubscriptionApi()
// val result = api.verifyAndroidPurchase(
// token = tokenStorage.getToken(),
// purchaseToken = purchaseToken,
// productId = productId
// )
println("BillingManager: Would verify purchase with backend")
}
/**
* Restore purchases
*/
suspend fun restorePurchases() {
// In production, this would query purchase history
// val result = billingClient.queryPurchasesAsync(
// QueryPurchasesParams.newBuilder()
// .setProductType(BillingClient.ProductType.SUBS)
// .build()
// )
println("BillingManager: Would restore purchases here")
}
/**
* Acknowledge purchase (required by Google Play)
*/
private suspend fun acknowledgePurchase(purchaseToken: String) {
// In production, this would acknowledge the purchase
// val params = AcknowledgePurchaseParams.newBuilder()
// .setPurchaseToken(purchaseToken)
// .build()
// billingClient.acknowledgePurchase(params)
println("BillingManager: Would acknowledge purchase")
}
}
/**
* Placeholder for ProductDetails
* In production, use com.android.billingclient.api.ProductDetails
*/
data class ProductDetails(
val productId: String,
val title: String,
val description: String,
val price: String
)