From b9aab92aee334acdffc6c0fde32101bb72be9a5e Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Sun, 2 Jul 2017 14:19:50 -0700 Subject: Add restore function --- .../kotlin/com/pitchedapps/frost/MainActivity.kt | 7 +- .../com/pitchedapps/frost/SettingsActivity.kt | 14 ++-- .../kotlin/com/pitchedapps/frost/utils/Prefs.kt | 1 - .../kotlin/com/pitchedapps/frost/utils/iab/IAB.kt | 91 ++++++++++++++++------ .../com/pitchedapps/frost/utils/iab/IABDialogs.kt | 8 ++ app/src/main/res/values/strings.xml | 4 + docs/Changelog.md | 2 + gradle.properties | 2 +- 8 files changed, 92 insertions(+), 37 deletions(-) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt index b4d7973e..f2fb9c29 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt @@ -44,6 +44,7 @@ import com.pitchedapps.frost.facebook.PROFILE_PICTURE_URL import com.pitchedapps.frost.fragments.WebFragment import com.pitchedapps.frost.utils.* import com.pitchedapps.frost.utils.iab.IAB +import com.pitchedapps.frost.utils.iab.validatePro import com.pitchedapps.frost.views.BadgedIcon import com.pitchedapps.frost.web.FrostWebViewSearch import io.reactivex.android.schedulers.AndroidSchedulers @@ -133,6 +134,7 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract { .setAction("Action", null).show() } setFrostColors(toolbar, themeWindow = false, headers = arrayOf(tabs, appBar), backgrounds = arrayOf(viewPager)) + validatePro() } fun tabsForEachView(action: (position: Int, view: BadgedIcon) -> Unit) { @@ -393,11 +395,6 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract { FbCookie.switchBackUser { } } - override fun onStart() { - super.onStart() - IAB.setupAsync(this) - } - override fun onBackPressed() { if (searchView?.onBackPressed() ?: false) return if (currentFragment.onBackPressed()) return diff --git a/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt index 0cdc5631..bf100895 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt @@ -2,6 +2,7 @@ package com.pitchedapps.frost import android.content.Intent import android.os.Bundle +import android.support.design.widget.Snackbar import android.view.Menu import android.view.MenuItem import ca.allanwang.kau.changelog.showChangelog @@ -70,16 +71,12 @@ 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) { iicon = GoogleMaterial.Icon.gmd_info - onClick = { - _, _, _ -> - startActivity(AboutActivity::class.java, transition = true) - true - } + onClick = { _, _, _ -> startActivity(AboutActivity::class.java, transition = true); true } } if (BuildConfig.DEBUG) { @@ -142,4 +139,9 @@ class SettingsActivity : KPrefActivity(), IabBroadcastReceiver.IabBroadcastListe } return true } + + override fun onDestroy() { + if (!IAB.isInProgress) IAB.dispose() + super.onDestroy() + } } \ No newline at end of file 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 e51bb0cb..f8060d9d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt @@ -1,6 +1,5 @@ package com.pitchedapps.frost.utils -import android.graphics.Color import ca.allanwang.kau.kotlin.lazyResettable import ca.allanwang.kau.kpref.KPref import ca.allanwang.kau.kpref.StringSet 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 b9213ae7..d511f773 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 @@ -3,13 +3,14 @@ package com.pitchedapps.frost.utils.iab import android.app.Activity import android.content.Context import android.content.Intent +import android.support.design.widget.Snackbar import ca.allanwang.kau.utils.isFromGooglePlay +import ca.allanwang.kau.utils.snackbar import com.crashlytics.android.answers.PurchaseEvent import com.pitchedapps.frost.BuildConfig +import com.pitchedapps.frost.R 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.* /** * Created by Allan Wang on 2017-06-23. @@ -27,13 +28,14 @@ object IAB { * and false otherwise * */ - operator fun invoke(activity: Activity, mustHavePlayStore: Boolean = true, onStart: (helper: IabHelper) -> Boolean) { + operator fun invoke(activity: Activity, mustHavePlayStore: Boolean = true, onFailed: () -> Unit = {}, onStart: (helper: IabHelper) -> Boolean) { with(activity) { if (helper?.mDisposed ?: true) { helper = null L.d("IAB setup async") if (!isFrostPlay) { if (mustHavePlayStore) playStoreNotFound() + onFailed() return } try { @@ -44,13 +46,17 @@ object IAB { if (result.isSuccess) { if (onStart(helper!!)) helper!!.disposeWhenFinished() - } else if (mustHavePlayStore) - activity.playStoreGenericError("Setup error: ${result.response} ${result.message}") + } else { + if (mustHavePlayStore) + activity.playStoreGenericError("Setup error: ${result.response} ${result.message}") + onFailed() + } } } catch (e: Exception) { L.e(e, "IAB error") if (mustHavePlayStore) playStoreGenericError(null) + onFailed() } } else if (onStart(helper!!)) helper!!.disposeWhenFinished() @@ -60,14 +66,21 @@ object IAB { fun handleActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean = helper?.handleActivityResult(requestCode, resultCode, data) ?: false + fun cancel() { + + } /** * Call this after any execution to dispose the helper + * Ensure that async calls have already finished beforehand */ fun dispose() { - helper?.disposeWhenFinished() + helper?.dispose() helper = null } + + val isInProgress: Boolean + get() = helper?.mAsyncInProgress ?: false } private const val FROST_PRO = "frost_pro" @@ -79,30 +92,60 @@ private val Context.isFrostPlay: Boolean get() = isFromGooglePlay || BuildConfig.DEBUG fun SettingsActivity.restorePurchases() { - validatePro(this) + //like validate, but with a snackbar and without other prompts + var restore: Snackbar? = null + restore = container.snackbar(R.string.restoring_purchases, Snackbar.LENGTH_INDEFINITE) { + setAction(R.string.kau_close) { restore?.dismiss() } + } + //called if inventory is not properly retrieved + val reset = { + if (Prefs.previouslyPro) { + Prefs.previouslyPro = false + Prefs.theme = Theme.DEFAULT.ordinal + } + finishRestore(restore) + } + getInventory(false, true, reset) { + val proSku = it.getSkuDetails(FROST_PRO) + Prefs.previouslyPro = proSku != null + finishRestore(restore) + } +} + +private fun SettingsActivity.finishRestore(snackbar: Snackbar?) { + snackbar?.dismiss() + materialDialogThemed { + title(R.string.purchases_restored) + content(if (Prefs.previouslyPro) R.string.purchases_restored_with_pro else R.string.purchases_restored_without_pro) + positiveText(R.string.reload) + dismissListener { adapter.notifyAdapterDataSetChanged() } + } } /** * 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 +fun Activity.validatePro() { + getInventory(Prefs.previouslyPro, true, { if (Prefs.previouslyPro) playStoreNoLongerPro() }) { + val proSku = it.getSkuDetails(FROST_PRO) + if (proSku == null && Prefs.previouslyPro) playStoreNoLongerPro() + else if (proSku != null && !Prefs.previouslyPro) playStoreFoundPro() + } +} + +fun Activity.getInventory( + mustHavePlayStore: Boolean = true, + disposeOnFinish: Boolean = true, + onFailed: () -> Unit = {}, + onSuccess: (inv: Inventory) -> Unit) { + IAB(this, mustHavePlayStore, onFailed) { 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() - } - } + helper.queryInventoryAsync { + res, inv -> + if (res.isFailure || inv == null) onFailed() + else onSuccess(inv) } - true + disposeOnFinish } } 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 330680f6..54355d3e 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 @@ -103,4 +103,12 @@ fun Activity.playStorePurchasedSuccessfully(key: String) { content(R.string.play_purchased_pro) positiveText(R.string.kau_ok) } +} + +fun SettingsActivity.purchaseRestored() { + materialDialogThemed { + title(R.string.play_thank_you) + content(R.string.play_purchased_pro) + positiveText(R.string.kau_ok) + } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 58c879d2..b79c6823 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -60,6 +60,10 @@ Looks like you\'ve already purchased %s. Enjoy! Found Frost Pro! Looks like you have frost pro! We\'ll reload the app so you can enjoy the awesome features! + Restoring purchases… + Purchases Restored + Frost Pro has been restored. Enjoy the features! + It seems like you don\'t have pro. If this is a persistent issue, contact me and attach your purchase receipt. diff --git a/docs/Changelog.md b/docs/Changelog.md index 96f5f1f5..6c000718 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,5 +1,7 @@ # Changelog +## v1.1 + ## v1.0 * Added more global preferences * Added fully customizable theme engine diff --git a/gradle.properties b/gradle.properties index 54baaf69..f0fa5e38 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ MIN_SDK=21 TARGET_SDK=26 BUILD_TOOLS=26.0.0 -KAU=e1e3b37000 +KAU=f4090285eb KOTLIN=1.1.3 MATERIAL_DRAWER=5.9.3 MATERIAL_DRAWER_KT=1.0.4 -- cgit v1.2.3