aboutsummaryrefslogtreecommitdiff
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
parentb148feff51d7f2805b1af378ad07740bfbeb53a8 (diff)
downloadfrost-8cc26f47b18bbc1944404d3378b885742a1d7586.tar.gz
frost-8cc26f47b18bbc1944404d3378b885742a1d7586.tar.bz2
frost-8cc26f47b18bbc1944404d3378b885742a1d7586.zip
Remap billing functionality
-rw-r--r--app/build.gradle11
-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
-rw-r--r--app/src/main/res/values/strings.xml5
-rw-r--r--app/src/main/res/xml/changelog.xml11
-rw-r--r--gradle.properties1
9 files changed, 134 insertions, 53 deletions
diff --git a/app/build.gradle b/app/build.gradle
index c8f40eb8..b64e5a74 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -11,6 +11,11 @@ android {
compileSdkVersion Integer.parseInt(project.TARGET_SDK)
buildToolsVersion project.BUILD_TOOLS
+ androidGitVersion {
+ codeFormat = 'MMNNPP'
+ prefix 'v'
+ }
+
defaultConfig {
applicationId "${project.APP_GROUP}." + project.APP_ID.toLowerCase()
minSdkVersion Integer.parseInt(project.MIN_SDK)
@@ -128,8 +133,4 @@ dependencies {
compile("com.crashlytics.sdk.android:crashlytics:${CRASHLYTICS}@aar") {
transitive = true;
}
-}
-
-androidGitVersion {
- prefix 'v'
-}
+} \ No newline at end of file
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)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d6c8afd7..58c879d2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -51,12 +51,15 @@
<string name="uh_oh">Uh Oh</string>
<string name="reload">Reload</string>
<string name="play_store_not_pro">It seems like you are a pro user, but we couldn\'t find your purchasing info. If this error persists, please try clearing the Play Store cache and reinstalling the app.</string>
- <string name="play_store_not_found">This is a pro feature, but this app doesn\'t seem to be installed from the Play Store. Please reinstall if this is an issue.</string>
+ <string name="play_store_not_found">This app doesn\'t seem to be installed from the Play Store. Please reinstall if this is an issue.</string>
+ <string name="play_store_not_found_pro_query">This is a pro feature, but this app doesn\'t seem to be installed from the Play Store. Please reinstall if this is an issue.</string>
<string name="play_store_billing_error">Something went wrong. Please try again later.</string>
<string name="play_thank_you">Thank you!</string>
<string name="play_purchased_pro">Thank you for your support! Enjoy the pro version.</string>
<string name="play_already_purchased">Already Purchased</string>
<string name="play_already_purchased_content">Looks like you\'ve already purchased %s. Enjoy!</string>
+ <string name="found_pro">Found Frost Pro!</string>
+ <string name="found_pro_desc">Looks like you have frost pro! We\'ll reload the app so you can enjoy the awesome features!</string>
<string name="define_dbflow"></string>
<!-- Author section -->
diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml
index e189577e..e916cdd5 100644
--- a/app/src/main/res/xml/changelog.xml
+++ b/app/src/main/res/xml/changelog.xml
@@ -6,6 +6,17 @@
<item text="" />
-->
+ <version title="v1.1"/>
+ <item text="" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+
<version title="v1.0"/>
<item text="Added more global preferences" />
diff --git a/gradle.properties b/gradle.properties
index 91f22293..54baaf69 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -16,7 +16,6 @@ APP_GROUP=com.pitchedapps
MIN_SDK=21
TARGET_SDK=26
BUILD_TOOLS=26.0.0
-VERSION_CODE=11
KAU=e1e3b37000
KOTLIN=1.1.3