diff options
-rw-r--r-- | app/build.gradle | 11 | ||||
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt | 8 | ||||
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt | 2 | ||||
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt | 4 | ||||
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IAB.kt | 114 | ||||
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt | 31 | ||||
-rw-r--r-- | app/src/main/res/values/strings.xml | 5 | ||||
-rw-r--r-- | app/src/main/res/xml/changelog.xml | 11 | ||||
-rw-r--r-- | gradle.properties | 1 |
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 |