Allow users to purchase gems right from insufficient-gems screen

This commit is contained in:
Phillip Thelen 2019-10-04 16:36:51 +02:00
parent 9dedc9d3be
commit 3a4b0e3925
13 changed files with 376 additions and 261 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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);
}

View file

@ -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]
}
}
}

View file

@ -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?)
}
}

View file

@ -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)
}
})
}
}
}

View file

@ -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

View file

@ -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()
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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>()

View file

@ -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)

View file

@ -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()
}
}