diff --git a/Habitica/build.gradle b/Habitica/build.gradle index f7ec3c9bd..aff6dc78d 100644 --- a/Habitica/build.gradle +++ b/Habitica/build.gradle @@ -153,7 +153,7 @@ android { multiDexEnabled true resConfigs "en", "bg", "de", "en-rGB", "es", "fr", "hr-rHR", "in", "it", "iw", "ja", "ko", "lt", "nl", "pl", "pt-rBR", "pt-rPT", "ru", "tr", "zh", "zh-rTW" - versionCode 2360 + versionCode 2362 versionName "2.5" } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt index e512fe1d3..db6cfab56 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.kt @@ -250,6 +250,9 @@ interface ApiService { @POST("/iap/android/subscribe") fun validateSubscription(@Body request: SubscriptionValidationRequest): Flowable> + @GET("/iap/android/subscribe/cancel") + fun cancelSubscription(): Flowable> + @POST("/iap/android/norenew-subscribe") fun validateNoRenewSubscription(@Body request: PurchaseValidationRequest): Flowable> diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt index 98a3d2177..6ae204161 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt @@ -63,6 +63,7 @@ interface ApiClient { fun purchaseQuest(key: String): Flowable fun validateSubscription(request: SubscriptionValidationRequest): Flowable fun validateNoRenewSubscription(request: PurchaseValidationRequest): Flowable + fun cancelSubscription(): Flowable fun sellItem(itemType: String, itemKey: String): Flowable diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt index 60f7dcaf6..309d16b10 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt @@ -347,6 +347,10 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener; } } + override fun cancelSubscription(): Flowable { + return apiService.cancelSubscription().compose(configureApiCallObserver()) + } + override fun purchaseHourglassItem(type: String, itemKey: String): Flowable { return apiService.purchaseHourglassItem(type, itemKey).compose(configureApiCallObserver()) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt index 548cf315d..ada17a27b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt @@ -115,6 +115,27 @@ class PurchaseHandler(activity: Activity, val crashlyticsProxy: CrashlyticsProxy } } + fun checkForSubscription(onSubscriptionFound: ((Purchase) -> Unit)) { + billingRequests?.getPurchases(ProductTypes.SUBSCRIPTION, null, object : RequestListener { + override fun onSuccess(result: Purchases) { + var lastPurchase: Purchase? = null + for (purchase in result.list) { + if (lastPurchase != null && lastPurchase.time > purchase.time) { + continue + } else { + lastPurchase = purchase + } + } + if (lastPurchase != null) { + onSubscriptionFound(lastPurchase) + } + } + + override fun onError(response: Int, e: java.lang.Exception) { + } + }) + } + private fun checkIfPendingPurchases() { billingRequests?.getAllPurchases(ProductTypes.IN_APP, object : RequestListener { override fun onSuccess(purchases: Purchases) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt index 13f67e6e1..d3fbda00e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/purchases/SubscriptionFragment.kt @@ -8,6 +8,7 @@ import android.view.ViewGroup import androidx.core.view.isVisible import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.UserComponent +import com.habitrpg.android.habitica.data.ApiClient import com.habitrpg.android.habitica.data.InventoryRepository import com.habitrpg.android.habitica.data.UserRepository import com.habitrpg.android.habitica.databinding.FragmentSubscriptionBinding @@ -26,6 +27,7 @@ import com.habitrpg.android.habitica.ui.views.subscriptions.SubscriptionOptionVi import io.reactivex.functions.Consumer import org.greenrobot.eventbus.Subscribe import org.solovyev.android.checkout.Inventory +import org.solovyev.android.checkout.Purchase import org.solovyev.android.checkout.Sku import javax.inject.Inject @@ -40,6 +42,8 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen lateinit var appConfigManager: AppConfigManager @Inject lateinit var inventoryRepository: InventoryRepository + @Inject + lateinit var apiClient: ApiClient private var selectedSubscriptionSku: Sku? = null private var skus: List = emptyList() @@ -48,6 +52,7 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen private var user: User? = null private var hasLoadedSubscriptionOptions: Boolean = false + private var purchasedSubscription: Purchase? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -82,17 +87,15 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen binding.refreshLayout.setOnRefreshListener { refresh() } - if (appConfigManager.useNewMysteryBenefits()) { - compositeSubscription.add(inventoryRepository.getLatestMysteryItem().subscribe(Consumer { - DataBindingUtils.loadImage(binding.subBenefitsMysteryItemIcon, "shop_set_mystery_${it.key?.split("_")?.last()}") - binding.subBenefitsMysteryItemText.text = context?.getString(R.string.subscribe_listitem3_description_new, it.text) - }, RxErrorHandler.handleEmptyError())) - } + compositeSubscription.add(inventoryRepository.getLatestMysteryItem().subscribe(Consumer { + DataBindingUtils.loadImage(binding.subBenefitsMysteryItemIcon, "shop_set_mystery_${it.key?.split("_")?.last()}") + binding.subBenefitsMysteryItemText.text = context?.getString(R.string.subscribe_listitem3_description_new, it.text) + }, RxErrorHandler.handleEmptyError())) } override fun onResume() { super.onResume() - refresh(); + refresh() } private fun refresh() { @@ -165,6 +168,11 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen override fun setPurchaseHandler(handler: PurchaseHandler?) { this.purchaseHandler = handler + + handler?.checkForSubscription { + purchasedSubscription = it + checkIfNeedsCancellation() + } } private fun purchaseSubscription() { @@ -178,6 +186,7 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen fun setUser(newUser: User) { user = newUser this.updateSubscriptionInfo() + checkIfNeedsCancellation() } private fun updateSubscriptionInfo() { @@ -208,10 +217,18 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen } } + private fun checkIfNeedsCancellation() { + if (user?.purchased?.plan?.isActive == true) { + compositeSubscription.add(apiClient.cancelSubscription().subscribe(Consumer { + refresh() + }, RxErrorHandler.handleEmptyError())) + } + } + private fun showSubscriptionOptions() { binding.subscriptionOptions.visibility = View.VISIBLE binding.subscriptionOptions.postDelayed({ - binding.scrollView.smoothScrollTo(0, binding.subscriptionOptions.top ?: 0) + binding.scrollView.smoothScrollTo(0, binding.subscriptionOptions.top) }, 500) }