aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-07-02 11:57:57 -0700
committerAllan Wang <me@allanwang.ca>2017-07-02 11:57:57 -0700
commit8cc26f47b18bbc1944404d3378b885742a1d7586 (patch)
tree22cd6fc95e396e61a892002821bd01e4fe78c4c9 /app/src/main/kotlin/com/pitchedapps
parentb148feff51d7f2805b1af378ad07740bfbeb53a8 (diff)
downloadfrost-8cc26f47b18bbc1944404d3378b885742a1d7586.tar.gz
frost-8cc26f47b18bbc1944404d3378b885742a1d7586.tar.bz2
frost-8cc26f47b18bbc1944404d3378b885742a1d7586.zip
Remap billing functionality
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt8
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt114
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt31
5 files changed, 113 insertions, 46 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt
index 0d084b51..0cdc5631 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt
@@ -24,14 +24,16 @@ import com.pitchedapps.frost.utils.iab.*
class SettingsActivity : KPrefActivity(), IabBroadcastReceiver.IabBroadcastListener {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- if (!(IAB.helper?.handleActivityResult(requestCode, resultCode, data) ?: false))
+ if (!IAB.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data)
- adapter.notifyDataSetChanged()
+ adapter.notifyDataSetChanged()
+ }
}
override fun receivedBroadcast() {
L.d("IAB broadcast")
+ adapter.notifyDataSetChanged()
}
override fun kPrefCoreAttributes(): CoreAttributeContract.() -> Unit = {
@@ -68,7 +70,7 @@ class SettingsActivity : KPrefActivity(), IabBroadcastReceiver.IabBroadcastListe
plainText(R.string.restore_purchases) {
descRes = R.string.restore_purchases
iicon = GoogleMaterial.Icon.gmd_refresh
- onClick = { this@SettingsActivity.restorePurchases(); true }
+ onClick = { _, _,_ -> this@SettingsActivity.restorePurchases(); true }
}
plainText(R.string.about_frost) {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt
index 60b0d2e9..b8bfb086 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt
@@ -22,7 +22,7 @@ fun SettingsActivity.getFeedPrefs(): KPrefAdapterBuilder.() -> Unit = {
title(R.string.newsfeed_sort)
items(FeedSort.values().map { string(it.textRes) })
itemsCallbackSingleChoice(item.pref, {
- _, _, which, text ->
+ _, _, which, _ ->
if (item.pref != which) {
item.pref = which
shouldRestartMain()
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
index cc815c55..e51bb0cb 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt
@@ -87,6 +87,10 @@ object Prefs : KPref() {
//check if this is the first time launching the web overlay; show snackbar if true
var firstWebOverlay: Boolean by kpref("first_web_overlay", true)
+ /**
+ * Cache like value to determine if user has or had pro
+ * In most cases, [com.pitchedapps.frost.utils.iab.IS_FROST_PRO] should be looked at instead
+ */
var previouslyPro: Boolean by kpref("previously_pro", false)
var debugPro: Boolean by kpref("debug_pro", false)
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt
index e047e2ff..b9213ae7 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt
@@ -2,6 +2,7 @@ package com.pitchedapps.frost.utils.iab
import android.app.Activity
import android.content.Context
+import android.content.Intent
import ca.allanwang.kau.utils.isFromGooglePlay
import com.crashlytics.android.answers.PurchaseEvent
import com.pitchedapps.frost.BuildConfig
@@ -9,39 +10,63 @@ import com.pitchedapps.frost.SettingsActivity
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.frostAnswers
-import com.pitchedapps.frost.utils.frostAnswersCustom
/**
* Created by Allan Wang on 2017-06-23.
*/
object IAB {
- var helper: IabHelper? = null
+ private var helper: IabHelper? = null
- fun setupAsync(activity: Activity) {
- if (helper == null) {
- L.d("IAB setup async")
- if (!activity.isFromGooglePlay && !BuildConfig.DEBUG) return L.d("IAB not from google play")
- try {
- helper = IabHelper(activity.applicationContext, PUBLIC_BILLING_KEY)
- helper!!.enableDebugLogging(BuildConfig.DEBUG, "Frost:")
- helper!!.startSetup {
- result ->
- L.d("IAB result ${result.message}")
- if (!result.isSuccess) L.eThrow("IAB Setup error: $result")
+ /**
+ * Wrapper function to ensure that the helper exists before executing a command
+ *
+ * [mustHavePlayStore] decides if dialogs should be shown if play store errors occur
+ *
+ * [onStart] should return true if we wish to dispose the helper after the operation
+ * and false otherwise
+ *
+ */
+ operator fun invoke(activity: Activity, mustHavePlayStore: Boolean = true, onStart: (helper: IabHelper) -> Boolean) {
+ with(activity) {
+ if (helper?.mDisposed ?: true) {
+ helper = null
+ L.d("IAB setup async")
+ if (!isFrostPlay) {
+ if (mustHavePlayStore) playStoreNotFound()
+ return
}
- } catch (e: Exception) {
- L.e(e, "IAB error")
- activity.playStoreNoLongerPro()
- }
+ try {
+ helper = IabHelper(applicationContext, PUBLIC_BILLING_KEY)
+ helper!!.enableDebugLogging(BuildConfig.DEBUG, "Frost:")
+ helper!!.startSetup {
+ result ->
+ if (result.isSuccess) {
+ if (onStart(helper!!))
+ helper!!.disposeWhenFinished()
+ } else if (mustHavePlayStore)
+ activity.playStoreGenericError("Setup error: ${result.response} ${result.message}")
+ }
+ } catch (e: Exception) {
+ L.e(e, "IAB error")
+ if (mustHavePlayStore)
+ playStoreGenericError(null)
+ }
+ } else if (onStart(helper!!))
+ helper!!.disposeWhenFinished()
}
}
+ fun handleActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean
+ = helper?.handleActivityResult(requestCode, resultCode, data) ?: false
+
+
/**
- * If user has pro, check if it's valid and destroy the helper
+ * Call this after any execution to dispose the helper
*/
- fun validatePro(activity: Activity) {
-
+ fun dispose() {
+ helper?.disposeWhenFinished()
+ helper = null
}
}
@@ -54,29 +79,51 @@ private val Context.isFrostPlay: Boolean
get() = isFromGooglePlay || BuildConfig.DEBUG
fun SettingsActivity.restorePurchases() {
+ validatePro(this)
+}
+/**
+ * If user has pro, check if it's valid and destroy the helper
+ */
+fun Activity.validatePro(activity: Activity) {
+ IAB(activity, Prefs.previouslyPro) { //if pro, ensure that it is in inventory; if not, check quietly if it exists
+ helper ->
+ with(activity) {
+ helper.queryInventoryAsync {
+ res, inv ->
+ if (res.isFailure) return@queryInventoryAsync playStoreGenericError("Query res error")
+ if (inv?.getSkuDetails(FROST_PRO) != null) {
+ //owns pro
+ if (!Prefs.previouslyPro)
+ playStoreFoundPro()
+ } else if (Prefs.previouslyPro) {
+ //doesn't own pro but has it
+ playStoreNoLongerPro()
+ }
+ }
+ }
+ true
+ }
}
-fun Activity.openPlayProPurchase(code: Int) = openPlayPurchase(FROST_PRO, code) {
- Prefs.previouslyPro = true
+fun Activity.openPlayProPurchase(code: Int) {
+ if (!IS_FROST_PRO)
+ playStoreProNotAvailable()
+ else openPlayPurchase(FROST_PRO, code) {
+ Prefs.previouslyPro = true
+ }
}
fun Activity.openPlayPurchase(key: String, code: Int, onSuccess: (key: String) -> Unit) {
L.d("Open play purchase $key $code")
- if (!isFrostPlay) return playStoreNotFound()
- frostAnswersCustom("PLAY_PURCHASE") {
- putCustomAttribute("Key", key)
- }
- L.d("IAB flag end async")
- IAB.helper?.flagEndAsync() ?: return playStoreGenericError("Null flag end async")
- L.d("IAB query inv async")
- try {
- IAB.helper!!.queryInventoryAsync {
+ IAB(this, true) {
+ helper ->
+ helper.queryInventoryAsync {
res, inv ->
if (res.isFailure) return@queryInventoryAsync playStoreGenericError("Query res error")
if (inv?.getSkuDetails(key) != null) return@queryInventoryAsync playStoreAlreadyPurchased(key)
L.d("IAB: inventory ${inv.allOwnedSkus}")
- IAB.helper!!.launchPurchaseFlow(this@openPlayPurchase, key, code) {
+ helper.launchPurchaseFlow(this@openPlayPurchase, key, code) {
result, _ ->
if (result.isSuccess) {
onSuccess(key)
@@ -90,7 +137,6 @@ fun Activity.openPlayPurchase(key: String, code: Int, onSuccess: (key: String) -
}
}
}
- } catch(e: IabHelper.IabAsyncInProgressException) {
- L.e(e, "IAB query dup")
+ false
}
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt
index 08958480..330680f6 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt
@@ -18,16 +18,18 @@ private fun playStoreLog(text: String) {
L.e(Throwable(text), "Play Store Exception")
}
+/**
+ * Properly restart an activity
+ */
private fun Activity.playRestart() {
- if (this is MainActivity) restart()
- else if (this is SettingsActivity) {
+ if (this is SettingsActivity) {
setResult(MainActivity.REQUEST_RESTART)
finish()
- }
+ } else restart()
}
+
fun Activity.playStoreNoLongerPro() {
- if (!Prefs.previouslyPro) return //never pro to begin with
Prefs.previouslyPro = false
playStoreLog("No Longer Pro")
materialDialogThemed {
@@ -40,6 +42,19 @@ fun Activity.playStoreNoLongerPro() {
}
}
+fun Activity.playStoreFoundPro() {
+ Prefs.previouslyPro = true
+ L.d("Found pro")
+ materialDialogThemed {
+ title(R.string.found_pro)
+ content(R.string.found_pro_desc)
+ positiveText(R.string.reload)
+ dismissListener {
+ this@playStoreFoundPro.playRestart()
+ }
+ }
+}
+
fun Activity.playStoreNotFound() {
L.d("Play store not found")
materialDialogThemed {
@@ -52,18 +67,18 @@ fun Activity.playStoreNotFound() {
}
fun Activity.playStoreProNotAvailable() {
- playStoreLog("Pro found; store not available")
+ playStoreLog("Pro query; store not available")
materialDialogThemed {
title(R.string.uh_oh)
- content(R.string.play_store_not_found)
+ content(R.string.play_store_not_found_pro_query)
positiveText(R.string.kau_ok)
neutralText(R.string.kau_play_store)
onNeutral { _, _ -> startPlayStoreLink(R.string.play_store_package_id) }
}
}
-fun Activity.playStoreGenericError(text: String = "Store generic error") {
- playStoreLog("IAB: $text")
+fun Activity.playStoreGenericError(text: String? = "Store generic error") {
+ if (text != null) playStoreLog("IAB: $text")
materialDialogThemed {
title(R.string.uh_oh)
content(R.string.play_store_billing_error)