mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-07-16 03:01:59 +00:00
Allow users to purchase gems right from insufficient-gems screen
This commit is contained in:
parent
9dedc9d3be
commit
3a4b0e3925
13 changed files with 376 additions and 261 deletions
|
|
@ -1,9 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical" android:layout_width="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -14,5 +15,33 @@
|
|||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"/>
|
||||
android:gravity="center_horizontal" />
|
||||
<LinearLayout
|
||||
android:id="@+id/purchase_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/layout_rounded_bg_brand_700"
|
||||
android:layout_marginTop="@dimen/spacing_medium"
|
||||
android:padding="@dimen/spacing_medium"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/gems_4" />
|
||||
<TextView
|
||||
android:id="@+id/purchase_textview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
style="@style/Subheader1"
|
||||
tools:text="4 Gems"
|
||||
android:gravity="center"/>
|
||||
<Button
|
||||
android:id="@+id/purchase_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="30dp"
|
||||
style="@style/HabiticaButton.Purple"
|
||||
android:minWidth="40dp"
|
||||
tools:text="$0.99" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
|
@ -847,4 +847,5 @@
|
|||
<string name="preference_push_party_activity">Party Activity</string>
|
||||
<string name="promo_subscription_buy_gems_prompt">Need Gems?</string>
|
||||
<string name="promo_subscription_buy_gems_description">Become a Subscriber to buy Gems with gold, get monthly mystery items, increased drop caps and more!</string>
|
||||
<string name="see_other_options">See other Options</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ import com.habitrpg.android.habitica.ui.fragments.tasks.TasksFragment;
|
|||
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel;
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.InboxViewModel;
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel;
|
||||
import com.habitrpg.android.habitica.ui.views.insufficientCurrency.InsufficientGemsDialog;
|
||||
import com.habitrpg.android.habitica.ui.views.shops.PurchaseDialog;
|
||||
import com.habitrpg.android.habitica.ui.views.social.ChatBarView;
|
||||
import com.habitrpg.android.habitica.ui.views.stats.BulkAllocateStatsDialog;
|
||||
|
|
@ -316,4 +317,6 @@ public interface UserComponent {
|
|||
void inject(@NotNull AchievementsFragment achievementsFragment);
|
||||
|
||||
void inject(@NotNull InboxViewModel inboxViewModel);
|
||||
|
||||
void inject(@NotNull InsufficientGemsDialog insufficientGemsDialog);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,207 @@
|
|||
package com.habitrpg.android.habitica.helpers
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.HabiticaPurchaseVerifier
|
||||
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy
|
||||
import org.solovyev.android.checkout.*
|
||||
import java.util.*
|
||||
|
||||
class PurchaseHandler(activity: Activity, val crashlyticsProxy: CrashlyticsProxy) {
|
||||
private val billing = HabiticaBaseApplication.getInstance(activity.applicationContext)?.billing
|
||||
private val checkout = billing?.let { Checkout.forActivity(activity, it) }
|
||||
private val inventory = checkout?.makeInventory()
|
||||
|
||||
private var billingRequests: BillingRequests? = null
|
||||
private var isStarted = false
|
||||
|
||||
var whenCheckoutReady: (() -> Unit)? = null
|
||||
|
||||
init {
|
||||
handlers[activity] = this
|
||||
}
|
||||
|
||||
fun startListening() {
|
||||
if (!isStarted) {
|
||||
checkout?.start()
|
||||
isStarted = true
|
||||
|
||||
checkout?.whenReady(object : Checkout.Listener {
|
||||
override fun onReady(requests: BillingRequests) {
|
||||
this@PurchaseHandler.billingRequests = requests
|
||||
checkIfPendingPurchases()
|
||||
whenCheckoutReady?.invoke()
|
||||
}
|
||||
|
||||
override fun onReady(requests: BillingRequests, product: String, billingSupported: Boolean) {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun stopListening() {
|
||||
if (isStarted) {
|
||||
checkout?.stop()
|
||||
isStarted = false
|
||||
}
|
||||
}
|
||||
|
||||
fun onResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
checkout?.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
fun getAllGemSKUs(onSuccess: ((List<Sku>) -> Unit)) {
|
||||
getSKUs(ProductTypes.IN_APP, PurchaseTypes.allGemTypes, onSuccess)
|
||||
}
|
||||
|
||||
fun getAllSubscriptionSKUs(onSuccess: ((List<Sku>) -> Unit)) {
|
||||
getSKUs(ProductTypes.SUBSCRIPTION, PurchaseTypes.allSubscriptionTypes, onSuccess)
|
||||
}
|
||||
|
||||
fun getAllSubscriptionProducts(onSuccess: ((Inventory.Product) -> Unit)) {
|
||||
getProduct(ProductTypes.SUBSCRIPTION, PurchaseTypes.allSubscriptionTypes, onSuccess)
|
||||
}
|
||||
|
||||
fun getAllGiftSubscriptionSKUs(onSuccess: ((List<Sku>) -> Unit)) {
|
||||
getSKUs(ProductTypes.IN_APP, PurchaseTypes.allSubscriptionNoRenewTypes, onSuccess)
|
||||
}
|
||||
|
||||
fun getInAppPurchaseSKU(identifier: String, onSuccess: ((Sku) -> Unit)) {
|
||||
getSKU(ProductTypes.IN_APP, identifier, onSuccess)
|
||||
}
|
||||
|
||||
fun getSubscriptionSKU(identifier: String, onSuccess: ((Sku) -> Unit)) {
|
||||
getSKU(ProductTypes.SUBSCRIPTION, identifier, onSuccess)
|
||||
}
|
||||
|
||||
|
||||
private fun getSKUs(type: String, identifiers: List<String>, onSuccess: ((List<Sku>) -> Unit)) {
|
||||
getProduct(type, identifiers) {
|
||||
onSuccess(it.skus)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getProduct(type: String, identifiers: List<String>, onSuccess: ((Inventory.Product) -> Unit)) {
|
||||
inventory?.load(Inventory.Request.create()
|
||||
.loadAllPurchases().loadSkus(type, identifiers)) { products ->
|
||||
val purchases = products.get(type)
|
||||
if (!purchases.supported) return@load
|
||||
onSuccess(purchases)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSKU(type: String, identifier: String, onSuccess: ((Sku) -> Unit)) {
|
||||
inventory?.load(Inventory.Request.create()
|
||||
.loadAllPurchases().loadSkus(type, listOf(identifier))) { products ->
|
||||
val purchases = products.get(type)
|
||||
if (!purchases.supported) return@load
|
||||
purchases.skus.firstOrNull()?.let { onSuccess(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun purchaseSubscription(sku: Sku) {
|
||||
sku.id.code?.let { code ->
|
||||
billingRequests?.isPurchased(ProductTypes.SUBSCRIPTION, code, object : RequestListener<Boolean> {
|
||||
override fun onSuccess(aBoolean: Boolean) {
|
||||
if (!aBoolean) {
|
||||
// no current product exist
|
||||
checkout?.let {
|
||||
billingRequests?.purchase(ProductTypes.SUBSCRIPTION, code, null, it.purchaseFlow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIfPendingPurchases() {
|
||||
billingRequests?.getAllPurchases(ProductTypes.IN_APP, object : RequestListener<Purchases> {
|
||||
override fun onSuccess(purchases: Purchases) {
|
||||
for (purchase in purchases.list) {
|
||||
if (PurchaseTypes.allGemTypes.contains(purchase.sku)) {
|
||||
billingRequests?.consume(purchase.token, object : RequestListener<Any> {
|
||||
override fun onSuccess(o: Any) {
|
||||
//EventBus.getDefault().post(new BoughtGemsEvent(GEMS_TO_ADD));
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("Purchase", "Consume", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("Purchase", "getAllPurchases", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun purchaseGems(identifier: String) {
|
||||
checkout?.let {
|
||||
it.destroyPurchaseFlow()
|
||||
billingRequests?.purchase(ProductTypes.IN_APP, identifier, null, it.createOneShotPurchaseFlow(object : RequestListener<Purchase> {
|
||||
override fun onSuccess(result: Purchase) {
|
||||
billingRequests?.consume(result.token, object : RequestListener<Any> {
|
||||
override fun onSuccess(o: Any) {
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("PurchaseConsumeException", "Consume", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(response: Int, e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fun purchaseGiftedSubscription(sku: Sku, giftedUserID: String?) {
|
||||
checkout?.let {
|
||||
HabiticaPurchaseVerifier.pendingGifts[sku.id.code] = giftedUserID
|
||||
billingRequests?.purchase(ProductTypes.IN_APP, sku.id.code, null, it.createOneShotPurchaseFlow(object : RequestListener<Purchase> {
|
||||
override fun onSuccess(result: Purchase) {
|
||||
billingRequests?.consume(result.token, object : RequestListener<Any> {
|
||||
override fun onSuccess(o: Any) {
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("PurchaseConsumeException", "Consume", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onError(response: Int, e: java.lang.Exception) {
|
||||
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fun consumePurchase(purchase: Purchase) {
|
||||
if (PurchaseTypes.allGemTypes.contains(purchase.sku) || PurchaseTypes.allSubscriptionNoRenewTypes.contains(purchase.sku)) {
|
||||
billingRequests?.consume(purchase.token, object : RequestListener<Any> {
|
||||
override fun onSuccess(result: Any) {}
|
||||
|
||||
override fun onError(response: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("PurchaseConsumeException", "Consume", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private var handlers = WeakHashMap<Activity, PurchaseHandler>()
|
||||
|
||||
fun findForActivity(activity: Activity): PurchaseHandler? {
|
||||
return handlers[activity]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,21 +3,18 @@ package com.habitrpg.android.habitica.ui.activities
|
|||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.events.ConsumablePurchasedEvent
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseTypes
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseHandler
|
||||
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy
|
||||
import com.habitrpg.android.habitica.ui.fragments.GemsPurchaseFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.SubscriptionFragment
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.solovyev.android.checkout.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class GemPurchaseActivity : BaseActivity() {
|
||||
|
|
@ -29,9 +26,7 @@ class GemPurchaseActivity : BaseActivity() {
|
|||
|
||||
internal var fragment: CheckoutFragment? = null
|
||||
var isActive = false
|
||||
var activityCheckout: ActivityCheckout? = null
|
||||
private set
|
||||
private var billingRequests: BillingRequests? = null
|
||||
var purchaseHandler: PurchaseHandler? = null
|
||||
|
||||
override fun getLayoutResId(): Int {
|
||||
return R.layout.activity_gem_purchase
|
||||
|
|
@ -43,7 +38,7 @@ class GemPurchaseActivity : BaseActivity() {
|
|||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
activityCheckout?.onActivityResult(requestCode, resultCode, data)
|
||||
purchaseHandler?.onResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -56,6 +51,8 @@ class GemPurchaseActivity : BaseActivity() {
|
|||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
supportActionBar?.setTitle(R.string.gem_purchase_toolbartitle)
|
||||
|
||||
purchaseHandler = PurchaseHandler(this, crashlyticsProxy)
|
||||
|
||||
if (intent.extras?.containsKey("openSubscription") == true) {
|
||||
if (intent.extras?.getBoolean("openSubscription") == false) {
|
||||
createFragment(false)
|
||||
|
|
@ -65,40 +62,15 @@ class GemPurchaseActivity : BaseActivity() {
|
|||
} else {
|
||||
createFragment(true)
|
||||
}
|
||||
|
||||
purchaseHandler?.whenCheckoutReady = {
|
||||
fragment?.setupCheckout()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setupCheckout()
|
||||
|
||||
activityCheckout?.destroyPurchaseFlow()
|
||||
|
||||
activityCheckout?.createPurchaseFlow(object : RequestListener<Purchase> {
|
||||
override fun onSuccess(purchase: Purchase) {
|
||||
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("PurchaseFlowException", "Error", e)
|
||||
val billingError = e as? BillingException
|
||||
if (billingError != null) {
|
||||
Log.e("BILLING ERROR", billingError.toString())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
activityCheckout?.whenReady(object : Checkout.Listener {
|
||||
override fun onReady(billingRequests: BillingRequests) {
|
||||
this@GemPurchaseActivity.billingRequests = billingRequests
|
||||
|
||||
fragment?.setBillingRequests(billingRequests)
|
||||
|
||||
checkIfPendingPurchases()
|
||||
}
|
||||
|
||||
override fun onReady(billingRequests: BillingRequests, s: String, b: Boolean) {}
|
||||
})
|
||||
purchaseHandler?.startListening()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
@ -112,7 +84,7 @@ class GemPurchaseActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
public override fun onStop() {
|
||||
activityCheckout?.stop()
|
||||
purchaseHandler?.stopListening()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
|
|
@ -124,25 +96,13 @@ class GemPurchaseActivity : BaseActivity() {
|
|||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun setupCheckout() {
|
||||
HabiticaBaseApplication.getInstance(this)?.billing?.let {
|
||||
activityCheckout = Checkout.forActivity(this, it)
|
||||
activityCheckout?.start()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun createFragment(showSubscription: Boolean) {
|
||||
val fragment: CheckoutFragment = if (showSubscription) {
|
||||
SubscriptionFragment()
|
||||
} else {
|
||||
GemsPurchaseFragment()
|
||||
}
|
||||
fragment.setListener(this@GemPurchaseActivity)
|
||||
fragment.setupCheckout()
|
||||
if (billingRequests != null) {
|
||||
fragment.setBillingRequests(billingRequests)
|
||||
}
|
||||
fragment.setPurchaseHandler(purchaseHandler)
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.fragment_container, fragment as Fragment)
|
||||
|
|
@ -150,58 +110,15 @@ class GemPurchaseActivity : BaseActivity() {
|
|||
this.fragment = fragment
|
||||
}
|
||||
|
||||
private fun checkIfPendingPurchases() {
|
||||
billingRequests?.getAllPurchases(ProductTypes.IN_APP, object : RequestListener<Purchases> {
|
||||
override fun onSuccess(purchases: Purchases) {
|
||||
for (purchase in purchases.list) {
|
||||
if (PurchaseTypes.allGemTypes.contains(purchase.sku)) {
|
||||
billingRequests?.consume(purchase.token, object : RequestListener<Any> {
|
||||
override fun onSuccess(o: Any) {
|
||||
//EventBus.getDefault().post(new BoughtGemsEvent(GEMS_TO_ADD));
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("Purchase", "Consume", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("Purchase", "getAllPurchases", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onConsumablePurchased(event: ConsumablePurchasedEvent) {
|
||||
if (isActive) {
|
||||
consumePurchase(event.purchase)
|
||||
purchaseHandler?.consumePurchase(event.purchase)
|
||||
}
|
||||
}
|
||||
|
||||
interface CheckoutFragment {
|
||||
|
||||
fun setupCheckout()
|
||||
|
||||
fun setListener(listener: GemPurchaseActivity)
|
||||
|
||||
fun setBillingRequests(billingRequests: BillingRequests?)
|
||||
}
|
||||
|
||||
private fun consumePurchase(purchase: Purchase) {
|
||||
if (PurchaseTypes.allGemTypes.contains(purchase.sku) || PurchaseTypes.allSubscriptionNoRenewTypes.contains(purchase.sku)) {
|
||||
billingRequests?.consume(purchase.token, object : RequestListener<Any> {
|
||||
|
||||
override fun onSuccess(result: Any) {
|
||||
|
||||
}
|
||||
|
||||
override fun onError(response: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("PurchaseConsumeException", "Consume", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
fun setPurchaseHandler(handler: PurchaseHandler?)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,13 @@ import android.widget.Button
|
|||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.view.isVisible
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.HabiticaPurchaseVerifier
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.data.SocialRepository
|
||||
import com.habitrpg.android.habitica.events.ConsumablePurchasedEvent
|
||||
import com.habitrpg.android.habitica.extensions.addOkButton
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseHandler
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseTypes
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy
|
||||
|
|
@ -28,7 +27,8 @@ import com.habitrpg.android.habitica.ui.views.social.UsernameLabel
|
|||
import com.habitrpg.android.habitica.ui.views.subscriptions.SubscriptionOptionView
|
||||
import io.reactivex.functions.Consumer
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.solovyev.android.checkout.*
|
||||
import org.solovyev.android.checkout.Inventory
|
||||
import org.solovyev.android.checkout.Sku
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
|
|
@ -41,9 +41,7 @@ class GiftIAPActivity: BaseActivity() {
|
|||
@Inject
|
||||
lateinit var appConfigManager: AppConfigManager
|
||||
|
||||
var activityCheckout: ActivityCheckout? = null
|
||||
private set
|
||||
private var billingRequests: BillingRequests? = null
|
||||
private var purchaseHandler: PurchaseHandler? = null
|
||||
|
||||
private val toolbar: Toolbar by bindView(R.id.toolbar)
|
||||
|
||||
|
|
@ -102,38 +100,8 @@ class GiftIAPActivity: BaseActivity() {
|
|||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setupCheckout()
|
||||
|
||||
activityCheckout?.destroyPurchaseFlow()
|
||||
|
||||
activityCheckout?.createPurchaseFlow(object : RequestListener<Purchase> {
|
||||
override fun onSuccess(purchase: Purchase) {
|
||||
if (PurchaseTypes.allSubscriptionNoRenewTypes.contains(purchase.sku)) {
|
||||
billingRequests?.consume(purchase.token, object : RequestListener<Any> {
|
||||
override fun onSuccess(o: Any) {
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("PurchaseConsumeException", "Consume", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("PurchaseFlowException", "Error", e)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
activityCheckout?.whenReady(object : Checkout.Listener {
|
||||
override fun onReady(billingRequests: BillingRequests) {
|
||||
this@GiftIAPActivity.billingRequests = billingRequests
|
||||
}
|
||||
|
||||
override fun onReady(billingRequests: BillingRequests, s: String, b: Boolean) {}
|
||||
})
|
||||
purchaseHandler = PurchaseHandler(this, crashlyticsProxy)
|
||||
purchaseHandler?.startListening()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
@ -146,13 +114,13 @@ class GiftIAPActivity: BaseActivity() {
|
|||
}
|
||||
|
||||
override fun onStop() {
|
||||
activityCheckout?.stop()
|
||||
purchaseHandler?.stopListening()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
activityCheckout?.onActivityResult(requestCode, resultCode, data)
|
||||
purchaseHandler?.onResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
|
@ -172,30 +140,6 @@ class GiftIAPActivity: BaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupCheckout() {
|
||||
HabiticaBaseApplication.getInstance(this)?.billing?.let {
|
||||
activityCheckout = Checkout.forActivity(this, it)
|
||||
activityCheckout?.start()
|
||||
}
|
||||
val checkout = activityCheckout
|
||||
if (checkout != null) {
|
||||
val inventory = checkout.makeInventory()
|
||||
|
||||
inventory.load(Inventory.Request.create()
|
||||
.loadAllPurchases().loadSkus(ProductTypes.IN_APP, PurchaseTypes.allSubscriptionNoRenewTypes)
|
||||
) { products ->
|
||||
val subscriptions = products.get(ProductTypes.IN_APP)
|
||||
|
||||
skus = subscriptions.skus
|
||||
|
||||
for (sku in skus) {
|
||||
updateButtonLabel(sku, sku.price, subscriptions)
|
||||
}
|
||||
selectSubscription(PurchaseTypes.Subscription1MonthNoRenew)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectSubscription(sku: String) {
|
||||
for (thisSku in skus) {
|
||||
if (thisSku.id.code == sku) {
|
||||
|
|
@ -235,16 +179,13 @@ class GiftIAPActivity: BaseActivity() {
|
|||
if (giftedUserID?.isNotEmpty() != true) {
|
||||
return
|
||||
}
|
||||
activityCheckout?.let {
|
||||
HabiticaPurchaseVerifier.pendingGifts[sku.id.code] = giftedUserID
|
||||
billingRequests?.purchase(ProductTypes.IN_APP, sku.id.code, null, it.purchaseFlow)
|
||||
}
|
||||
purchaseHandler?.purchaseGiftedSubscription(sku, giftedUserID)
|
||||
}
|
||||
|
||||
|
||||
@Subscribe
|
||||
fun onConsumablePurchased(event: ConsumablePurchasedEvent) {
|
||||
consumePurchase(event.purchase)
|
||||
purchaseHandler?.consumePurchase(event.purchase)
|
||||
runOnUiThread {
|
||||
displayConfirmationDialog()
|
||||
}
|
||||
|
|
@ -275,18 +216,4 @@ class GiftIAPActivity: BaseActivity() {
|
|||
}
|
||||
alert.enqueue()
|
||||
}
|
||||
|
||||
private fun consumePurchase(purchase: Purchase) {
|
||||
if (PurchaseTypes.allGemTypes.contains(purchase.sku) || PurchaseTypes.allSubscriptionNoRenewTypes.contains(purchase.sku)) {
|
||||
billingRequests?.consume(purchase.token, object : RequestListener<Any> {
|
||||
|
||||
override fun onSuccess(result: Any) {
|
||||
}
|
||||
|
||||
override fun onError(response: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("PurchaseConsumeException", "Consume", e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -406,6 +406,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
|
|||
data.getStringExtra("notificationId")
|
||||
)
|
||||
}
|
||||
PurchaseHandler.findForActivity(this)?.onResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
// region Events
|
||||
|
|
@ -740,6 +741,11 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
|
|||
}.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onConsumablePurchased(event: ConsumablePurchasedEvent) {
|
||||
userRepository.retrieveUser(false, true).subscribe(Consumer {}, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SELECT_CLASS_RESULT = 11
|
||||
const val GEM_PURCHASE_REQUEST = 111
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int): Recycle
|
|||
items.indexOfFirst { it.identifier == identifier }
|
||||
|
||||
fun updateItem(item: HabiticaDrawerItem) {
|
||||
val position = getItemPosition(item.transitionId)
|
||||
val position = getItemPosition(item.identifier)
|
||||
items[position] = item
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.habitrpg.android.habitica.R
|
|||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.extensions.inflate
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseHandler
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseTypes
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy
|
||||
|
|
@ -19,9 +20,6 @@ import com.habitrpg.android.habitica.ui.helpers.bindView
|
|||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
import com.habitrpg.android.habitica.ui.views.promo.SubscriptionBuyGemsPromoView
|
||||
import io.reactivex.functions.Consumer
|
||||
import org.solovyev.android.checkout.BillingRequests
|
||||
import org.solovyev.android.checkout.Inventory
|
||||
import org.solovyev.android.checkout.ProductTypes
|
||||
import javax.inject.Inject
|
||||
|
||||
class GemsPurchaseFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragment {
|
||||
|
|
@ -38,8 +36,7 @@ class GemsPurchaseFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
|
|||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
|
||||
private var listener: GemPurchaseActivity? = null
|
||||
private var billingRequests: BillingRequests? = null
|
||||
private var purchaseHandler: PurchaseHandler? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?): View? {
|
||||
|
|
@ -70,32 +67,15 @@ class GemsPurchaseFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
|
|||
}
|
||||
|
||||
override fun setupCheckout() {
|
||||
val checkout = listener?.activityCheckout
|
||||
if (checkout != null) {
|
||||
val inventory = checkout.makeInventory()
|
||||
|
||||
inventory.load(Inventory.Request.create()
|
||||
.loadAllPurchases().loadSkus(ProductTypes.IN_APP, PurchaseTypes.allGemTypes)
|
||||
) { products ->
|
||||
val gems = products.get(ProductTypes.IN_APP)
|
||||
if (!gems.supported) {
|
||||
// billing is not supported, user can't purchase anything
|
||||
return@load
|
||||
}
|
||||
val skus = gems.skus
|
||||
for (sku in skus) {
|
||||
updateButtonLabel(sku.id.code, sku.price)
|
||||
}
|
||||
purchaseHandler?.getAllGemSKUs { skus ->
|
||||
for (sku in skus) {
|
||||
updateButtonLabel(sku.id.code, sku.price)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setListener(listener: GemPurchaseActivity) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
override fun setBillingRequests(billingRequests: BillingRequests?) {
|
||||
this.billingRequests = billingRequests
|
||||
override fun setPurchaseHandler(handler: PurchaseHandler?) {
|
||||
this.purchaseHandler = handler
|
||||
}
|
||||
|
||||
private fun updateButtonLabel(sku: String, price: String) {
|
||||
|
|
@ -112,9 +92,7 @@ class GemsPurchaseFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
|
|||
}
|
||||
}
|
||||
|
||||
fun purchaseGems(sku: String) {
|
||||
listener?.activityCheckout?.let {
|
||||
billingRequests?.purchase(ProductTypes.IN_APP, sku, null, it.purchaseFlow)
|
||||
}
|
||||
fun purchaseGems(identifier: String) {
|
||||
purchaseHandler?.purchaseGems(identifier)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.habitrpg.android.habitica.data.UserRepository
|
|||
import com.habitrpg.android.habitica.events.UserSubscribedEvent
|
||||
import com.habitrpg.android.habitica.extensions.addCancelButton
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseHandler
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseTypes
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
|
|
@ -30,7 +31,8 @@ import com.habitrpg.android.habitica.ui.views.subscriptions.SubscriptionOptionVi
|
|||
import io.reactivex.functions.Consumer
|
||||
import kotlinx.android.synthetic.main.fragment_subscription.*
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.solovyev.android.checkout.*
|
||||
import org.solovyev.android.checkout.Inventory
|
||||
import org.solovyev.android.checkout.Sku
|
||||
import javax.inject.Inject
|
||||
|
||||
class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragment {
|
||||
|
|
@ -77,8 +79,7 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
|
|||
private var selectedSubscriptionSku: Sku? = null
|
||||
private var skus: List<Sku> = emptyList()
|
||||
|
||||
private var listener: GemPurchaseActivity? = null
|
||||
private var billingRequests: BillingRequests? = null
|
||||
private var purchaseHandler: PurchaseHandler? = null
|
||||
|
||||
private var user: User? = null
|
||||
private var hasLoadedSubscriptionOptions: Boolean = false
|
||||
|
|
@ -140,24 +141,13 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
|
|||
}
|
||||
|
||||
override fun setupCheckout() {
|
||||
val checkout = listener?.activityCheckout
|
||||
if (checkout != null) {
|
||||
val inventory = checkout.makeInventory()
|
||||
|
||||
inventory.load(Inventory.Request.create()
|
||||
.loadAllPurchases().loadSkus(ProductTypes.SUBSCRIPTION, PurchaseTypes.allSubscriptionTypes)
|
||||
) { products ->
|
||||
val subscriptions = products.get(ProductTypes.SUBSCRIPTION)
|
||||
|
||||
skus = subscriptions.skus
|
||||
|
||||
for (sku in skus) {
|
||||
updateButtonLabel(sku, sku.price, subscriptions)
|
||||
}
|
||||
selectSubscription(PurchaseTypes.Subscription1Month)
|
||||
hasLoadedSubscriptionOptions = true
|
||||
updateSubscriptionInfo()
|
||||
purchaseHandler?.getAllSubscriptionProducts {subscriptions ->
|
||||
for (sku in skus) {
|
||||
updateButtonLabel(sku, sku.price, subscriptions)
|
||||
}
|
||||
selectSubscription(PurchaseTypes.Subscription1Month)
|
||||
hasLoadedSubscriptionOptions = true
|
||||
updateSubscriptionInfo()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -206,31 +196,13 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen
|
|||
}
|
||||
}
|
||||
|
||||
override fun setListener(listener: GemPurchaseActivity) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
override fun setBillingRequests(billingRequests: BillingRequests?) {
|
||||
this.billingRequests = billingRequests
|
||||
override fun setPurchaseHandler(handler: PurchaseHandler?) {
|
||||
this.purchaseHandler = handler
|
||||
}
|
||||
|
||||
private fun purchaseSubscription() {
|
||||
selectedSubscriptionSku?.id?.code?.let { code ->
|
||||
billingRequests?.isPurchased(ProductTypes.SUBSCRIPTION, code, object : RequestListener<Boolean> {
|
||||
override fun onSuccess(aBoolean: Boolean) {
|
||||
if (!aBoolean) {
|
||||
// no current product exist
|
||||
val checkout = listener?.activityCheckout
|
||||
checkout?.let {
|
||||
billingRequests?.purchase(ProductTypes.SUBSCRIPTION, code, null, it.purchaseFlow)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(i: Int, e: Exception) {
|
||||
crashlyticsProxy.fabricLogE("Purchase", "Error", e)
|
||||
}
|
||||
})
|
||||
selectedSubscriptionSku?.let { sku ->
|
||||
purchaseHandler?.purchaseSubscription(sku)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.ui.views.dialogs
|
|||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.view.ContextThemeWrapper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
|
@ -208,6 +209,14 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
|
|||
super.dismiss()
|
||||
}
|
||||
|
||||
fun getActivity(): Activity? {
|
||||
var thisContext = context
|
||||
while (thisContext as? ContextThemeWrapper != null && thisContext as? Activity == null) {
|
||||
thisContext = thisContext.baseContext
|
||||
}
|
||||
return thisContext as? Activity
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var dialogQueue = mutableListOf<HabiticaAlertDialog>()
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,13 @@ abstract class InsufficientCurrencyDialog(context: Context) : HabiticaAlertDialo
|
|||
protected var imageView: ImageView
|
||||
protected var textView: TextView
|
||||
|
||||
open var layoutID = R.layout.dialog_insufficient_currency
|
||||
open fun getLayoutID(): Int {
|
||||
return R.layout.dialog_insufficient_currency
|
||||
}
|
||||
|
||||
init {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val view = inflater.inflate(layoutID, contentView, false)
|
||||
val view = inflater.inflate(getLayoutID(), contentView, false)
|
||||
setAdditionalContentView(view)
|
||||
|
||||
imageView = view.findViewById(R.id.imageView)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,23 @@
|
|||
package com.habitrpg.android.habitica.ui.views.insufficientCurrency
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.os.bundleOf
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.events.ConsumablePurchasedEvent
|
||||
import com.habitrpg.android.habitica.extensions.addCloseButton
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseHandler
|
||||
import com.habitrpg.android.habitica.helpers.PurchaseTypes
|
||||
import com.habitrpg.android.habitica.proxy.CrashlyticsProxy
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Created by phillip on 27.09.17.
|
||||
|
|
@ -12,13 +25,64 @@ import com.habitrpg.android.habitica.helpers.MainNavigationController
|
|||
|
||||
class InsufficientGemsDialog(context: Context) : InsufficientCurrencyDialog(context) {
|
||||
|
||||
override var layoutID: Int = R.layout.dialog_insufficient_gems
|
||||
private var purchaseButton: Button? = null
|
||||
@Inject
|
||||
lateinit var configManager: AppConfigManager
|
||||
@Inject
|
||||
lateinit var crashlyticsProxy: CrashlyticsProxy
|
||||
|
||||
private var purchaseHandler: PurchaseHandler? = null
|
||||
|
||||
override fun getLayoutID(): Int {
|
||||
return R.layout.dialog_insufficient_gems
|
||||
}
|
||||
|
||||
init {
|
||||
HabiticaBaseApplication.userComponent?.inject(this)
|
||||
imageView.setImageResource(R.drawable.gems_84)
|
||||
textView.setText(R.string.insufficientGems)
|
||||
|
||||
addButton(R.string.purchase_gems, true) { _, _ -> MainNavigationController.navigate(R.id.gemPurchaseActivity, bundleOf(Pair("openSubscription", true))) }
|
||||
|
||||
addButton(R.string.see_other_options, false) { _, _ -> MainNavigationController.navigate(R.id.gemPurchaseActivity, bundleOf(Pair("openSubscription", false))) }
|
||||
addCloseButton()
|
||||
if (!configManager.insufficientGemPurchase()) {
|
||||
contentView.findViewWithTag<LinearLayout>(R.id.purchase_wrapper).visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
if (configManager.insufficientGemPurchase()) {
|
||||
getActivity()?.let {
|
||||
purchaseButton = contentView.findViewById(R.id.purchase_button)
|
||||
purchaseHandler = PurchaseHandler(it, crashlyticsProxy)
|
||||
purchaseHandler?.startListening()
|
||||
purchaseHandler?.whenCheckoutReady = {
|
||||
purchaseHandler?.getInAppPurchaseSKU(PurchaseTypes.Purchase4Gems) {sku ->
|
||||
val purchaseTextView = contentView.findViewById<TextView>(R.id.purchase_textview)
|
||||
purchaseTextView.text = sku.displayTitle
|
||||
purchaseButton?.text = sku.price
|
||||
}
|
||||
}
|
||||
|
||||
purchaseButton?.setOnClickListener {
|
||||
purchaseHandler?.purchaseGems(PurchaseTypes.Purchase4Gems)
|
||||
}
|
||||
}
|
||||
}
|
||||
EventBus.getDefault().register(this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
if (EventBus.getDefault().isRegistered(this)) {
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
fun onConsumablePurchased(event: ConsumablePurchasedEvent) {
|
||||
purchaseHandler?.consumePurchase(event.purchase)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue