diff options
author | Allan Wang <me@allanwang.ca> | 2017-06-30 20:32:33 -0700 |
---|---|---|
committer | Allan Wang <me@allanwang.ca> | 2017-06-30 20:32:33 -0700 |
commit | 1811531d6934f4eadfca70b30afa2dac76400bef (patch) | |
tree | 7b9a3fec0a840f602ee2be371c9c224211c4b426 /app/src | |
parent | 8760cb29b8a37d15a1dcba018054d4010b90df21 (diff) | |
download | frost-1811531d6934f4eadfca70b30afa2dac76400bef.tar.gz frost-1811531d6934f4eadfca70b30afa2dac76400bef.tar.bz2 frost-1811531d6934f4eadfca70b30afa2dac76400bef.zip |
Test more billing
Diffstat (limited to 'app/src')
10 files changed, 231 insertions, 50 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt index 67ce8f7c..b9bb52d8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt @@ -1,14 +1,25 @@ package com.pitchedapps.frost +import android.graphics.Color +import android.support.constraint.ConstraintLayout +import android.support.v7.widget.RecyclerView +import android.view.View +import android.widget.ImageView import ca.allanwang.kau.about.AboutActivityBase import ca.allanwang.kau.adapters.FastItemThemedAdapter -import ca.allanwang.kau.iitems.CardIItem -import ca.allanwang.kau.logging.KL -import ca.allanwang.kau.utils.isColorVisibleOn -import ca.allanwang.kau.utils.withMinAlpha +import ca.allanwang.kau.adapters.ThemableIItem +import ca.allanwang.kau.adapters.ThemableIItemDelegate +import ca.allanwang.kau.iitems.LibraryIItem +import ca.allanwang.kau.utils.* +import ca.allanwang.kau.views.createSimpleRippleDrawable import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.entity.Library +import com.mikepenz.aboutlibraries.entity.License +import com.mikepenz.community_material_typeface_library.CommunityMaterial import com.mikepenz.fastadapter.IItem +import com.mikepenz.fastadapter.items.AbstractItem +import com.mikepenz.google_material_typeface_library.GoogleMaterial +import com.mikepenz.iconics.typeface.IIcon import com.pitchedapps.frost.utils.Prefs @@ -51,8 +62,65 @@ class AboutActivity : AboutActivityBase(R.string::class.java, configBuilder = { } override fun postInflateMainPage(adapter: FastItemThemedAdapter<IItem<*, *>>) { - adapter.add(CardIItem { - descRes = R.string.frost_description - }) + /** + * Frost may not be a library but we're conveying the same info + */ + val frost = Library().apply { + libraryName = string(R.string.app_name) + author = "Pitched Apps" + libraryWebsite = "https://github.com/AllanWang/Frost-for-Facebook" + isOpenSource = true + libraryDescription = string(R.string.frost_description) + libraryVersion = BuildConfig.VERSION_NAME + license = License().apply { + licenseName = "GNU GPL v3" + licenseWebsite = "https://www.gnu.org/licenses/gpl-3.0.en.html" + } + } + adapter.add(LibraryIItem(frost)).add(AboutLinks()) + + } + + class AboutLinks : AbstractItem<AboutLinks, AboutLinks.ViewHolder>(), ThemableIItem by ThemableIItemDelegate() { + override fun getViewHolder(v: View): ViewHolder = ViewHolder(v) + + override fun getType(): Int = R.id.item_about_links + + override fun getLayoutRes(): Int = R.layout.item_about_links + + override fun bindView(holder: ViewHolder, payloads: MutableList<Any>?) { + super.bindView(holder, payloads) + with(holder) { + bindIconColor(rate, github) + bindBackgroundColor(container) + } + } + + fun theme(vararg images: ImageView) { + val ripple = createSimpleRippleDrawable(accentColor!!, Color.TRANSPARENT) + images.forEach { + it.background = ripple + it.drawable.setTint(textColor!!) + } + } + + class ViewHolder(v: View) : RecyclerView.ViewHolder(v) { + + val container: ConstraintLayout by bindView(R.id.about_icons_container) + val rate: ImageView by bindView(R.id.about_rate) + val github: ImageView by bindView(R.id.about_github) + + init { + val c = itemView.context + setup(rate, GoogleMaterial.Icon.gmd_star, { c.startPlayStoreLink(R.string.play_store_package_id) }) + setup(github, CommunityMaterial.Icon.cmd_github_circle, { c.startLink("https://github.com/AllanWang/Frost-for-Facebook") }) + } + + fun setup(image: ImageView, icon: IIcon, onClick: () -> Unit) { + image.setImageDrawable(icon.toDrawable(itemView.context, 32)) + image.setOnClickListener({ onClick() }) + } + + } } }
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt index a221859f..4dce7d4a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt @@ -12,8 +12,8 @@ import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.utils.CrashReportingTree -import com.pitchedapps.frost.utils.iab.IAB import com.pitchedapps.frost.utils.Prefs +import com.pitchedapps.frost.utils.iab.IAB import com.raizlabs.android.dbflow.config.FlowConfig import com.raizlabs.android.dbflow.config.FlowManager import io.fabric.sdk.android.Fabric @@ -36,11 +36,7 @@ class FrostApp : Application() { override fun onCreate() { FlowManager.init(FlowConfig.Builder(this).build()) Prefs.initialize(this, "${BuildConfig.APPLICATION_ID}.prefs") - FbCookie() - if (Prefs.installDate == -1L) Prefs.installDate = System.currentTimeMillis() - if (Prefs.identifier == -1) Prefs.identifier = Random().nextInt(Int.MAX_VALUE) - Prefs.lastLaunch = System.currentTimeMillis() -// if (LeakCanary.isInAnalyzerProcess(this)) return + // if (LeakCanary.isInAnalyzerProcess(this)) return // refWatcher = LeakCanary.install(this) if (BuildConfig.DEBUG) { Timber.plant(DebugTree()) @@ -50,10 +46,15 @@ class FrostApp : Application() { Crashlytics.setUserIdentifier(Prefs.frostId) Timber.plant(CrashReportingTree()) } + FbCookie() + if (Prefs.installDate == -1L) Prefs.installDate = System.currentTimeMillis() + if (Prefs.identifier == -1) Prefs.identifier = Random().nextInt(Int.MAX_VALUE) + Prefs.lastLaunch = System.currentTimeMillis() + + super.onCreate() - IAB.setupAsync(this) //Drawer profile loading logic DrawerImageLoader.init(object : AbstractDrawerImageLoader() { override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt index dcb68696..9094c58e 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt @@ -1,7 +1,11 @@ package com.pitchedapps.frost +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Context import android.content.Intent import android.graphics.drawable.ColorDrawable +import android.os.Build import android.os.Bundle import android.support.annotation.StringRes import android.support.design.widget.* @@ -39,6 +43,7 @@ import com.pitchedapps.frost.facebook.FbTab 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.views.BadgedIcon import com.pitchedapps.frost.web.FrostWebViewSearch import io.reactivex.android.schedulers.AndroidSchedulers @@ -84,6 +89,7 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract { const val REQUEST_REFRESH = 80808 const val REQUEST_NAV = 10101 const val REQUEST_SEARCH = 70707 + const val REQUEST_RESTART_APPLICATION = 60606 } override fun onCreate(savedInstanceState: Bundle?) { @@ -365,6 +371,18 @@ class MainActivity : BaseActivity(), FrostWebViewSearch.SearchContract { REQUEST_REFRESH -> webFragmentObservable.onNext(FRAGMENT_REFRESH) REQUEST_NAV -> frostNavigationBar() REQUEST_SEARCH -> invalidateOptionsMenu() + REQUEST_RESTART_APPLICATION -> { //completely restart application + val intent = packageManager.getLaunchIntentForPackage(packageName) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + val pending = PendingIntent.getActivity(this, 666, intent, PendingIntent.FLAG_CANCEL_CURRENT) + val alarm = getSystemService(Context.ALARM_SERVICE) as AlarmManager + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) + alarm.setExactAndAllowWhileIdle(AlarmManager.RTC, System.currentTimeMillis() + 100, pending) + else + alarm.setExact(AlarmManager.RTC, System.currentTimeMillis() + 100, pending) + finish() + System.exit(0) + } } } } @@ -374,6 +392,11 @@ 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/settings/Experimental.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt index 054b3669..fcb22320 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt @@ -22,4 +22,14 @@ fun SettingsActivity.getExperimentalPrefs(): KPrefAdapterBuilder.() -> Unit = { checkbox(R.string.verbose_logging, { Prefs.verboseLogging }, { Prefs.verboseLogging = it }) { descRes = R.string.verbose_logging_desc } + + plainText(R.string.restart_frost) { + descRes = R.string.restart_frost_desc + onClick = { + _, _, _ -> + setResult(MainActivity.REQUEST_RESTART_APPLICATION) + finish() + true + } + } }
\ No newline at end of file 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 19b9b6f7..0fd10c5b 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 @@ -16,17 +16,20 @@ object IAB { var helper: IabHelper? = null - fun setupAsync(context: Context) { - if (!context.isFromGooglePlay) return + 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(context.applicationContext, PUBLIC_BILLING_KEY) + helper = IabHelper(activity.applicationContext, PUBLIC_BILLING_KEY) helper!!.startSetup { result -> + L.d("IAB result ${result.message}") if (!result.isSuccess) L.eThrow("IAB Setup error: $result") } } catch (e: Exception) { L.e(e, "IAB error") + activity.playStoreNoLongerPro() } } } @@ -35,7 +38,7 @@ object IAB { private const val FROST_PRO = "frost_pro" val IS_FROST_PRO: Boolean - get() = (BuildConfig.DEBUG && Prefs.debugPro) || (IAB.helper?.queryInventory()?.getSkuDetails(FROST_PRO) != null) + get() = (BuildConfig.DEBUG && Prefs.debugPro) || Prefs.previouslyPro private fun Context.checkFromPlay(): Boolean { val isPlay = isFromGooglePlay || BuildConfig.DEBUG @@ -52,44 +55,32 @@ private fun Context.checkFromPlay(): Boolean { fun Activity.openPlayProPurchase(code: Int) = openPlayPurchase(FROST_PRO, code) fun Activity.openPlayPurchase(key: String, code: Int) { + L.d("Open play purchase $key $code") if (!checkFromPlay()) return frostAnswersCustom("PLAY_PURCHASE") { putCustomAttribute("Key", key) } - IAB.helper?.flagEndAsync() ?: playStoreErrorDialog() - IAB.helper?.queryInventoryAsync { + L.d("IAB flag end async") + IAB.helper?.flagEndAsync() ?: return playStoreGenericError("Null flag end async") + L.d("IAB query inv async") + IAB.helper!!.queryInventoryAsync { res, inv -> - if (res.isFailure) { - L.e("IAB error: ${res.message}") - playStoreErrorDialog() - } else if (inv == null) { - playStoreErrorDialog("Empty inventory") - } else { - val donation = inv.getSkuDetails(key) - if (donation != null) { - IAB.helper?.launchPurchaseFlow(this@openPlayPurchase, donation.sku, code) { - result, _ -> - if (result.isSuccess) materialDialogThemed { - title(R.string.play_thank_you) - content(R.string.play_purchased_pro) - positiveText(R.string.kau_ok) - } else playStoreErrorDialog("Result: ${result.message}") - frostAnswers { - logPurchase(PurchaseEvent() - .putItemId(key) - .putSuccess(result.isSuccess)) - } - } ?: playStoreErrorDialog("Launch Purchase Flow") + if (res.isFailure) return@queryInventoryAsync playStoreGenericError("Query res error") + if (inv == null) return@queryInventoryAsync playStoreGenericError("Empty inventory") + L.d("IAB: inventory ${inv.allOwnedSkus}") + val donation = inv.getSkuDetails(key) ?: return@queryInventoryAsync playStoreGenericError("Donation null") + IAB.helper!!.launchPurchaseFlow(this@openPlayPurchase, donation.sku, code) { + result, _ -> + if (result.isSuccess) materialDialogThemed { + title(R.string.play_thank_you) + content(R.string.play_purchased_pro) + positiveText(R.string.kau_ok) + } else playStoreGenericError("Result: ${result.message}") + frostAnswers { + logPurchase(PurchaseEvent() + .putItemId(key) + .putSuccess(result.isSuccess)) } } } -} - -private fun Context.playStoreErrorDialog(s: String = "Play Store Error") { - materialDialogThemed { - title(R.string.uh_oh) - content(R.string.play_store_billing_error) - positiveText(R.string.kau_ok) - } - L.e(Throwable(s), "Play Store Error") }
\ 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 new file mode 100644 index 00000000..e855138f --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt @@ -0,0 +1,50 @@ +package com.pitchedapps.frost.utils.iab + +import android.app.Activity +import ca.allanwang.kau.utils.restart +import ca.allanwang.kau.utils.startPlayStoreLink +import com.pitchedapps.frost.R +import com.pitchedapps.frost.utils.L +import com.pitchedapps.frost.utils.Prefs +import com.pitchedapps.frost.utils.materialDialogThemed + +/** + * Created by Allan Wang on 2017-06-30. + */ +private fun playStoreLog(text: String) { + L.e(Throwable(text), "Play Store Exception") +} + +fun Activity.playStoreNoLongerPro() { + if (!Prefs.previouslyPro) return //never pro to begin with + Prefs.previouslyPro = false + playStoreLog("No Longer Pro") + materialDialogThemed { + title(R.string.uh_oh) + content(R.string.play_store_not_pro) + positiveText(R.string.reload) + dismissListener { + this@playStoreNoLongerPro.restart() + } + } +} + +fun Activity.playStoreNotAvailable() { + playStoreLog("Store not available") + materialDialogThemed { + title(R.string.uh_oh) + content(R.string.play_store_not_found) + 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") + materialDialogThemed { + title(R.string.uh_oh) + content(R.string.play_store_billing_error) + positiveText(R.string.kau_ok) + } +}
\ No newline at end of file diff --git a/app/src/main/res/layout/item_about_links.xml b/app/src/main/res/layout/item_about_links.xml new file mode 100644 index 00000000..db831835 --- /dev/null +++ b/app/src/main/res/layout/item_about_links.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/about_icons_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:clickable="false" + android:padding="@dimen/kau_spacing_normal"> + + <ImageView + android:id="@+id/about_rate" + android:layout_width="@dimen/kau_avatar_bounds" + android:layout_height="@dimen/kau_avatar_bounds" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:scaleType="center" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/about_github" + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <ImageView + android:id="@id/about_github" + android:layout_width="@dimen/kau_avatar_bounds" + android:layout_height="@dimen/kau_avatar_bounds" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:scaleType="center" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/about_rate" + app:layout_constraintTop_toTopOf="parent" /> + +</android.support.constraint.ConstraintLayout>
\ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index 9ce1b4e3..a0bb0a87 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -2,4 +2,5 @@ <resources> <item name="item_account" type="id" /> <item name="item_keyword" type="id" /> + <item name="item_about_links" type="id" /> </resources>
\ 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 5e4fe3ba..ec7a6cf6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -49,6 +49,8 @@ <string name="pro_features">Pro Features</string> <string name="custom_pro">Custom [Pro]</string> <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_billing_error">Something went wrong. Please try again later.</string> <string name="play_thank_you">Thank you!</string> diff --git a/app/src/main/res/values/strings_preferences b/app/src/main/res/values/strings_preferences index afa41943..1c20b810 100644 --- a/app/src/main/res/values/strings_preferences +++ b/app/src/main/res/values/strings_preferences @@ -54,5 +54,7 @@ <string name="search_desc">Enable the search bar instead of a search overlay</string> <string name="verbose_logging">Verbose Logging</string> <string name="verbose_logging_desc">Enable verbose logging to help with crash reports. Logging will only be sent once an error is encountered, so repeat the issue to notify the dev.</string> + <string name="restart_frost">Restart Frost</string> + <string name="restart_frost_desc">Crashlytics will only submit logs when a crash occurs or if errors are found and the app is restarted. Clicking here will restart the app and flush whatever issues are currently found.</string> </resources>
\ No newline at end of file |