From ec5e6b427ebf213d03259ee3a5807d7a90b72837 Mon Sep 17 00:00:00 2001 From: Laurent Date: Thu, 10 Sep 2020 14:18:47 +0200 Subject: [PATCH] Upgrade billing API to 3.0 --- app/build.gradle | 8 +- app/src/main/AndroidManifest.xml | 1 - .../android/ui/fragment/SettingsFragment.kt | 9 +-- .../android/ui/fragment/StatisticsFragment.kt | 16 ++-- .../ui/fragment/SubscriptionFragment.kt | 15 ++-- .../ui/fragment/components/RealmFragment.kt | 38 +++------ .../calendar/CalendarDetailsFragment.kt | 4 +- .../ui/modules/calendar/CalendarFragment.kt | 15 ++-- .../ui/modules/data/DataManagerFragment.kt | 4 +- .../ui/modules/datalist/DataListActivity.kt | 22 ++--- .../ui/modules/datalist/DataListFragment.kt | 8 +- .../modules/filter/FilterDetailsFragment.kt | 4 +- .../ui/modules/filter/FiltersActivity.kt | 4 +- .../ui/modules/filter/FiltersFragment.kt | 6 +- .../ui/modules/filter/FiltersListActivity.kt | 4 +- .../handhistory/editor/EditorFragment.kt | 7 +- .../handhistory/replayer/ReplayerFragment.kt | 4 +- .../ui/modules/session/SessionActivity.kt | 4 +- .../ui/modules/session/SessionFragment.kt | 7 +- .../android/util/billing/AppGuard.kt | 81 ++++++++++++++----- .../android/util/billing/Security.kt | 25 +++--- .../android/util/csv/CSVImporter.kt | 4 +- app/src/main/res/values/strings.xml | 1 + 23 files changed, 152 insertions(+), 139 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 814b84aa..affea5d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,8 +34,8 @@ android { applicationId "net.pokeranalytics.android" minSdkVersion 23 targetSdkVersion 29 - versionCode 105 - versionName "5.0.7" + versionCode 108 + versionName "5.0.8" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -112,9 +112,7 @@ dependencies { implementation 'com.google.android.libraries.places:places:2.3.0' // Billing / Subscriptions - // WARNING FOR 2.0: https://developer.android.com/google/play/billing/billing_library_releases_notes - // Purchases MUST BE ACKNOWLEDGED - implementation 'com.android.billingclient:billing:1.2.2' + implementation 'com.android.billingclient:billing:3.0.0' // Firebase implementation 'com.google.firebase:firebase-core:17.5.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8ffe53ad..7951a288 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,6 @@ - diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt index 09cfd829..111cee96 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt @@ -47,7 +47,6 @@ import java.util.* class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRepresentableDataSource { - companion object { /** @@ -143,7 +142,7 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep if (!AppGuard.isProUser) { BillingActivity.newInstanceForResult(this, false) } else { - this.openPlaystoreAccount() + this.openPlayStoreAccount() } } SettingRow.RATE_APP -> showReviewManager() @@ -175,8 +174,8 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep private fun showReviewManager() { val manager = ReviewManagerFactory.create(requireContext()) - val request = manager.requestReviewFlow() - request.addOnCompleteListener { request -> + val task = manager.requestReviewFlow() + task.addOnCompleteListener { request -> if (request.isSuccessful) { // We got the ReviewInfo object val reviewInfo = request.result @@ -243,7 +242,7 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep /** * Open Google Play account */ - private fun openPlaystoreAccount() { + private fun openPlayStoreAccount() { val packageName = "net.pokeranalytics.android" val sku = IAPProducts.PRO.identifier diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt index 60ed311d..dc4913ee 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatisticsFragment.kt @@ -7,7 +7,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import io.realm.Realm -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.launch @@ -59,7 +58,7 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener { initUI() this.currentFilterable = FilterableType.SESSION applyFilter() - listenAsynchronously(this, ComputableResult::class.java) + listenRealmChanges(this, ComputableResult::class.java) } private fun initUI() { @@ -117,13 +116,14 @@ class StatisticsFragment : FilterableFragment(), RealmAsyncListener { // Business override fun asyncListenedEntityChange(realm: Realm) { + launchStatComputation() - val report = createSessionGroupsAndStartCompute(realm) - tableReportFragment.report = report - - GlobalScope.launch(Dispatchers.Main) { - tableReportFragment.showResults() - } +// val report = createSessionGroupsAndStartCompute(realm) +// tableReportFragment.report = report +// +// GlobalScope.launch(Dispatchers.Main) { +// tableReportFragment.showResults() +// } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SubscriptionFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SubscriptionFragment.kt index 2c413b07..503464c1 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/SubscriptionFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/SubscriptionFragment.kt @@ -21,10 +21,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentStatePagerAdapter import androidx.viewpager.widget.ViewPager -import com.android.billingclient.api.BillingClient -import com.android.billingclient.api.Purchase -import com.android.billingclient.api.SkuDetails -import com.android.billingclient.api.SkuDetailsResponseListener +import com.android.billingclient.api.* import com.crashlytics.android.Crashlytics import kotlinx.android.synthetic.main.fragment_subscription.* import net.pokeranalytics.android.R @@ -128,7 +125,7 @@ class SubscriptionFragment : BaseFragment(), SkuDetailsResponseListener, Purchas // Pager // The pager adapter, which provides the pages to the view pager widget. - this.pagerAdapter = ScreenSlidePagerAdapter(requireFragmentManager()) + this.pagerAdapter = ScreenSlidePagerAdapter(parentFragmentManager) this.pager.adapter = pagerAdapter this.pager.addOnPageChangeListener(this) @@ -196,10 +193,10 @@ class SubscriptionFragment : BaseFragment(), SkuDetailsResponseListener, Purchas } // SkuDetailsResponseListener - override fun onSkuDetailsResponse(responseCode: Int, skuDetailsList: MutableList?) { - if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null) { + override fun onSkuDetailsResponse(result: BillingResult, skuDetailsList: MutableList?) { + if (result.responseCode == BillingClient.BillingResponseCode.OK) { this.hideLoader() - selectedProduct = skuDetailsList.first { it.sku == IAPProducts.PRO.identifier } + selectedProduct = skuDetailsList?.firstOrNull { it.sku == IAPProducts.PRO.identifier } updateUI() } } @@ -223,6 +220,8 @@ class SubscriptionFragment : BaseFragment(), SkuDetailsResponseListener, Purchas val formattedFreeTrial = "$freeTrialDays " + requireContext().getString(R.string.days) + " " + requireContext().getString(R.string.free_trial) this.freetrial.text = formattedFreeTrial + } ?: run { + Toast.makeText(requireContext(), R.string.contact_support, Toast.LENGTH_LONG).show() } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt index 69b06f25..c24bd928 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/components/RealmFragment.kt @@ -1,7 +1,6 @@ package net.pokeranalytics.android.ui.fragment.components import android.os.Bundle -import android.os.Looper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -9,9 +8,6 @@ import io.realm.Realm import io.realm.RealmModel import io.realm.RealmResults import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import kotlinx.coroutines.launch import timber.log.Timber import kotlin.coroutines.CoroutineContext @@ -29,17 +25,12 @@ open class RealmFragment : BaseFragment() { */ private lateinit var realm: Realm - /*** - * A background realm instance - */ - private var asyncRealm: Realm? = null - /*** * A listener to async updates */ - private var asyncListener: RealmAsyncListener? = null + private var changeListener: RealmAsyncListener? = null - private var asyncResults: RealmResults? = null + private var realmResults: RealmResults? = null /** * A List of observed RealmResults @@ -62,24 +53,14 @@ open class RealmFragment : BaseFragment() { return super.onCreateView(inflater, container, savedInstanceState) } - fun listenAsynchronously(listener: RealmAsyncListener, clazz: Class) { + fun listenRealmChanges(listener: RealmAsyncListener, clazz: Class) { - this.asyncListener = listener + this.changeListener = listener - GlobalScope.launch(coroutineContext) { - val async = GlobalScope.async { - Looper.prepare() // a looper is required on the thread to listen to change - Looper.loop() - val realm = Realm.getDefaultInstance() - - asyncResults = realm.where(clazz).findAll() - asyncResults?.addChangeListener { t, _ -> - Timber.d("Realm changes: ${asyncResults?.size}, ${this@RealmFragment}") - asyncListener?.asyncListenedEntityChange(t.realm) - } - asyncRealm = realm - } - async.await() + this.realmResults = this.realm.where(clazz).findAllAsync() + this.realmResults?.addChangeListener { t, _ -> + Timber.d("Realm changes: ${realmResults?.size}, $this") + this.changeListener?.asyncListenedEntityChange(t.realm) } } @@ -92,8 +73,7 @@ open class RealmFragment : BaseFragment() { } this.realm.close() - this.asyncResults?.removeAllChangeListeners() - this.asyncRealm?.close() + this.realmResults?.removeAllChangeListeners() } /** diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt index 37387757..a918a6e3 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt @@ -7,7 +7,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import com.github.mikephil.charting.data.BarDataSet import com.github.mikephil.charting.data.LineDataSet @@ -40,7 +40,7 @@ import kotlin.collections.ArrayList class CalendarDetailsFragment : BaseFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { private val model: CalendarDetailsViewModel by lazy { - ViewModelProviders.of(requireActivity()).get(CalendarDetailsViewModel::class.java) + ViewModelProvider(requireActivity()).get(CalendarDetailsViewModel::class.java) } companion object { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarFragment.kt index 6bd0eaa9..cf10446c 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarFragment.kt @@ -83,7 +83,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable super.onViewCreated(view, savedInstanceState) initData() initUI() - listenAsynchronously(this, ComputableResult::class.java) + listenRealmChanges(this, ComputableResult::class.java) } override fun adapterRows(): List? { @@ -417,12 +417,15 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable } override fun asyncListenedEntityChange(realm: Realm) { - Timber.d("asyncListenedEntityChange") - launchStatComputation(realm) - activity?.runOnUiThread { - displayData() - } + launchAsyncStatComputation() + +// Timber.d("asyncListenedEntityChange") +// launchStatComputation(realm) +// +// activity?.runOnUiThread { +// displayData() +// } } } \ No newline at end of file diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/data/DataManagerFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/data/DataManagerFragment.kt index a6b34ad4..85928927 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/data/DataManagerFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/data/DataManagerFragment.kt @@ -8,7 +8,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.appcompat.app.AlertDialog -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import net.pokeranalytics.android.R import net.pokeranalytics.android.exceptions.ConfigurationException import net.pokeranalytics.android.model.interfaces.Savable @@ -20,7 +20,7 @@ import net.pokeranalytics.android.ui.viewmodel.DataManagerViewModel open class DataManagerFragment : RealmFragment() { protected val model: DataManagerViewModel by lazy { - ViewModelProviders.of(this).get(DataManagerViewModel::class.java) + ViewModelProvider(this).get(DataManagerViewModel::class.java) } // lateinit var item: Deletable diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/datalist/DataListActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/datalist/DataListActivity.kt index d2229f82..8db4b214 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/datalist/DataListActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/datalist/DataListActivity.kt @@ -4,7 +4,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import net.pokeranalytics.android.R import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.ui.activity.components.BaseActivity @@ -14,7 +14,7 @@ import net.pokeranalytics.android.ui.modules.filter.FilterActivityRequestCode class DataListActivity : BaseActivity() { val model: DataListViewModel by lazy { - ViewModelProviders.of(this).get(DataListViewModel::class.java) + ViewModelProvider(this).get(DataListViewModel::class.java) } enum class IntentKey(val keyName: String) { @@ -37,15 +37,15 @@ class DataListActivity : BaseActivity() { ) } - fun newSelectInstance(fragment: Fragment, dataType: Int, showAddButton: Boolean = true) { - val context = fragment.requireContext() - fragment.startActivityForResult( - getIntent( - context, - dataType, - showAddButton - ), FilterActivityRequestCode.SELECT_FILTER.ordinal) - } +// fun newSelectInstance(fragment: Fragment, dataType: Int, showAddButton: Boolean = true) { +// val context = fragment.requireContext() +// fragment.startActivityForResult( +// getIntent( +// context, +// dataType, +// showAddButton +// ), FilterActivityRequestCode.SELECT_FILTER.ordinal) +// } fun newInstance(fragment: Fragment, dataType: LiveData, selection: Boolean, itemIds: Array? = null, showAddButton: Boolean = true) { val context = fragment.requireContext() diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/datalist/DataListFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/datalist/DataListFragment.kt index 6d36e830..1973cdd6 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/datalist/DataListFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/datalist/DataListFragment.kt @@ -6,7 +6,7 @@ import android.os.Bundle import android.view.* import androidx.appcompat.widget.SearchView import androidx.core.view.isVisible -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import io.realm.Realm @@ -18,14 +18,14 @@ import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.model.interfaces.Deletable import net.pokeranalytics.android.model.interfaces.Identifiable import net.pokeranalytics.android.model.realm.Filter -import net.pokeranalytics.android.ui.modules.data.EditableDataActivity -import net.pokeranalytics.android.ui.modules.filter.FiltersActivity import net.pokeranalytics.android.ui.activity.components.RequestCode import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.extensions.removeMargins import net.pokeranalytics.android.ui.fragment.components.DeletableItemFragment import net.pokeranalytics.android.ui.helpers.SwipeToDeleteCallback +import net.pokeranalytics.android.ui.modules.data.EditableDataActivity +import net.pokeranalytics.android.ui.modules.filter.FiltersActivity import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.util.extensions.find import net.pokeranalytics.android.util.extensions.sorted @@ -34,7 +34,7 @@ import net.pokeranalytics.android.util.extensions.sorted open class DataListFragment : DeletableItemFragment(), RowRepresentableDelegate { val model: DataListViewModel by lazy { - ViewModelProviders.of(requireActivity()).get(DataListViewModel::class.java) + ViewModelProvider(requireActivity()).get(DataListViewModel::class.java) } companion object { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt index f8795bce..5e5794a7 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FilterDetailsFragment.kt @@ -5,7 +5,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.synthetic.main.fragment_filter_details.* import kotlinx.android.synthetic.main.fragment_filter_details.view.* @@ -28,7 +28,7 @@ import kotlin.collections.ArrayList open class FilterDetailsFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate { val model: FilterViewModel by lazy { - ViewModelProviders.of(requireActivity()).get(FilterViewModel::class.java) + ViewModelProvider(requireActivity()).get(FilterViewModel::class.java) } private lateinit var rowRepresentableAdapter: RowRepresentableAdapter diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersActivity.kt index 28ec56ce..8bb9d5a8 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersActivity.kt @@ -4,7 +4,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import net.pokeranalytics.android.R import net.pokeranalytics.android.ui.activity.components.BaseActivity import net.pokeranalytics.android.ui.fragment.components.BaseFragment @@ -12,7 +12,7 @@ import net.pokeranalytics.android.ui.fragment.components.BaseFragment class FiltersActivity : BaseActivity() { val model: FilterViewModel by lazy { - ViewModelProviders.of(this).get(FilterViewModel::class.java) + ViewModelProvider(this).get(FilterViewModel::class.java) } enum class IntentKey(val keyName: String) { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt index 9177d529..88b5894a 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersFragment.kt @@ -5,7 +5,7 @@ import android.content.Intent import android.os.Bundle import android.view.* import androidx.core.view.isVisible -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.chip.Chip import kotlinx.android.synthetic.main.fragment_editable_data.appBar @@ -29,7 +29,7 @@ import timber.log.Timber open class FiltersFragment : RealmFragment(), RowRepresentableDelegate { val model: FilterViewModel by lazy { - ViewModelProviders.of(requireActivity()).get(FilterViewModel::class.java) + ViewModelProvider(requireActivity()).get(FilterViewModel::class.java) } companion object { @@ -94,8 +94,6 @@ open class FiltersFragment : RealmFragment(), RowRepresentableDelegate { override fun onBackPressed() { if (this.model.isUpdating) { cancelUpdates() - } else { -// activity?.finish() } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersListActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersListActivity.kt index 3ac7d569..22efb843 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersListActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/filter/FiltersListActivity.kt @@ -4,7 +4,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import net.pokeranalytics.android.R import net.pokeranalytics.android.model.LiveData import net.pokeranalytics.android.ui.activity.components.BaseActivity @@ -13,7 +13,7 @@ import net.pokeranalytics.android.ui.modules.datalist.DataListViewModel class FiltersListActivity : BaseActivity() { val model: DataListViewModel by lazy { - ViewModelProviders.of(this).get(DataListViewModel::class.java) + ViewModelProvider(this).get(DataListViewModel::class.java) } enum class IntentKey(val keyName: String) { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/editor/EditorFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/editor/EditorFragment.kt index 51c18ae8..a0e7310d 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/editor/EditorFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/editor/EditorFragment.kt @@ -10,7 +10,7 @@ import android.view.* import android.view.animation.AccelerateDecelerateInterpolator import androidx.constraintlayout.widget.ConstraintLayout import androidx.lifecycle.Observer -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import kotlinx.android.synthetic.main.fragment_hand_history.* import kotlinx.android.synthetic.main.fragment_settings.recyclerView import net.pokeranalytics.android.R @@ -27,7 +27,6 @@ import net.pokeranalytics.android.ui.extensions.px import net.pokeranalytics.android.ui.extensions.showAlertDialog import net.pokeranalytics.android.ui.fragment.components.BaseFragment import net.pokeranalytics.android.ui.fragment.components.RealmFragment -import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType import net.pokeranalytics.android.ui.modules.datalist.DataListActivity import net.pokeranalytics.android.ui.modules.handhistory.HandHistoryActivity @@ -87,7 +86,7 @@ class EditorFragment : RealmFragment(), RowRepresentableDelegate, KeyboardListen super.onCreate(savedInstanceState) this.model = activity?.run { - ViewModelProviders.of(this)[EditorViewModel::class.java] + ViewModelProvider(this)[EditorViewModel::class.java] } ?: throw Exception("Invalid Activity") } @@ -153,7 +152,7 @@ class EditorFragment : RealmFragment(), RowRepresentableDelegate, KeyboardListen val observer = Observer> { this.editorAdapter.notifyDataSetChanged() } - this.model.rowsLiveData.observe(this, observer) + this.model.rowsLiveData.observe(viewLifecycleOwner, observer) // At first, the selection is defined before the holder is bound, // so we retrieve the editText inputConnection once the recycler view has been rendered diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt index d2e0a644..a58975dc 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerFragment.kt @@ -6,7 +6,7 @@ import android.os.Looper import android.view.* import android.widget.PopupWindow import android.widget.Switch -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import io.realm.Sort import kotlinx.android.synthetic.main.fragment_replayer.* import net.pokeranalytics.android.R @@ -42,7 +42,7 @@ class ReplayerFragment : RealmFragment() { super.onCreate(savedInstanceState) this.model = activity?.run { - ViewModelProviders.of(this)[ReplayerModel::class.java] + ViewModelProvider(this)[ReplayerModel::class.java] } ?: throw Exception("Invalid Activity") } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionActivity.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionActivity.kt index cd4b4cea..6f37ee95 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionActivity.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionActivity.kt @@ -5,7 +5,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import net.pokeranalytics.android.R import net.pokeranalytics.android.ui.activity.components.BaseActivity @@ -14,7 +14,7 @@ class SessionActivity: BaseActivity() { private lateinit var sessionFragment: SessionFragment val model: SessionViewModel by lazy { - ViewModelProviders.of(this).get(SessionViewModel::class.java) + ViewModelProvider(this).get(SessionViewModel::class.java) } enum class IntentKey(val keyName : String) { diff --git a/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt index b680cc8a..20f0d407 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/modules/session/SessionFragment.kt @@ -7,11 +7,10 @@ import android.view.animation.OvershootInterpolator import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.interpolator.view.animation.FastOutSlowInInterpolator -import androidx.lifecycle.ViewModelProviders +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DiffUtil import com.crashlytics.android.Crashlytics import kotlinx.android.synthetic.main.fragment_session.* -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.launch @@ -45,8 +44,6 @@ import net.pokeranalytics.android.util.extensions.formattedHourlyDuration import net.pokeranalytics.android.util.extensions.getNextMinuteInMilliseconds import timber.log.Timber import java.util.* -import kotlin.coroutines.CoroutineContext - class SessionFragment : RealmFragment(), RowRepresentableDelegate { @@ -93,7 +90,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate { super.onCreate(savedInstanceState) this.viewModel = activity?.run { - ViewModelProviders.of(this).get(SessionViewModel::class.java) + ViewModelProvider(this).get(SessionViewModel::class.java) } ?: throw Exception("Invalid Activity") } diff --git a/app/src/main/java/net/pokeranalytics/android/util/billing/AppGuard.kt b/app/src/main/java/net/pokeranalytics/android/util/billing/AppGuard.kt index 0412b4ac..fdff8f86 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/billing/AppGuard.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/billing/AppGuard.kt @@ -5,6 +5,7 @@ import android.content.Context import android.os.Handler import android.os.Looper import com.android.billingclient.api.* +import com.android.billingclient.api.BillingClient.BillingResponseCode import net.pokeranalytics.android.BuildConfig import net.pokeranalytics.android.R import timber.log.Timber @@ -99,7 +100,7 @@ object AppGuard : PurchasesUpdatedListener { */ fun load(context: Context) { - billingClient = BillingClient.newBuilder(context).setListener(this).build() + billingClient = BillingClient.newBuilder(context).setListener(this).enablePendingPurchases().build() this.startConnection(Runnable { this.updatePurchases() @@ -110,10 +111,9 @@ object AppGuard : PurchasesUpdatedListener { private fun startConnection(executeOnSuccess: Runnable) { billingClient.startConnection(object : BillingClientStateListener { - override fun onBillingSetupFinished(@BillingClient.BillingResponse billingResponseCode: Int) { - - this@AppGuard.billingResponseCode = billingResponseCode - if (billingResponseCode == BillingClient.BillingResponse.OK) { + override fun onBillingSetupFinished(result: BillingResult) { + this@AppGuard.billingResponseCode = result.responseCode + if (billingResponseCode == BillingResponseCode.OK) { // The BillingClient is ready. You can query purchases here. billingClientAvailable = true executeOnSuccess.run() @@ -125,6 +125,22 @@ object AppGuard : PurchasesUpdatedListener { } }) +// billingClient.startConnection(object : BillingClientStateListener { +// override fun onBillingSetupFinished(@BillingClient.BillingResponse billingResponseCode: Int) { +// +// this@AppGuard.billingResponseCode = billingResponseCode +// if (billingResponseCode == BillingClient.BillingResponse.OK) { +// // The BillingClient is ready. You can query purchases here. +// billingClientAvailable = true +// executeOnSuccess.run() +// } +// } +// +// override fun onBillingServiceDisconnected() { +// billingClientAvailable = false +// } +// }) + } private fun executeServiceRequest(runnable: Runnable) { @@ -150,8 +166,8 @@ object AppGuard : PurchasesUpdatedListener { this.resetPurchases() // Automatically checks for purchases (when switching devices for example) val purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.SUBS) - if (purchasesResult != null && purchasesResult.purchasesList != null) { - purchasesResult.purchasesList.forEach { + if (purchasesResult != null) { + purchasesResult.purchasesList?.forEach { this.handlePurchase(it) } } @@ -206,23 +222,40 @@ object AppGuard : PurchasesUpdatedListener { // PurchasesUpdatedListener - /** - * Purchase callback - */ - override fun onPurchasesUpdated(responseCode: Int, purchases: MutableList?) { + override fun onPurchasesUpdated(result: BillingResult, purchases: MutableList?) { - if (responseCode == BillingClient.BillingResponse.OK && purchases != null) { + if (result.responseCode == BillingResponseCode.OK && purchases != null) { for (purchase in purchases) { handlePurchase(purchase) } - } else if (responseCode == BillingClient.BillingResponse.USER_CANCELED) { + } else if (result.responseCode == BillingResponseCode.USER_CANCELED) { + Timber.d("purchase cancel message: ${result.debugMessage}") // Handle an error caused by a user cancelling the purchase flow. } else { + Timber.d("purchase error message: ${result.debugMessage}") // Handle any other error codes. } + } +// /** +// * Purchase callback +// */ +// override fun onPurchasesUpdated(responseCode: Int, purchases: MutableList?) { +// +// if (responseCode == BillingClient.BillingResponse.OK && purchases != null) { +// for (purchase in purchases) { +// handlePurchase(purchase) +// } +// } else if (responseCode == BillingClient.BillingResponse.USER_CANCELED) { +// // Handle an error caused by a user cancelling the purchase flow. +// } else { +// // Handle any other error codes. +// } +// +// } + /** * Method called when a [purchase] has been made */ @@ -232,18 +265,28 @@ object AppGuard : PurchasesUpdatedListener { when (purchase.sku) { IAPProducts.PRO.identifier -> { - val date = Date(purchase.purchaseTime) Timber.d("*** Auto renewing = ${purchase.isAutoRenewing}") Timber.d("*** purchaseTime = $date") - this._isProUser = true - this.purchaseDelegate?.let { - Handler(Looper.getMainLooper()).post { - it.purchaseDidSucceed(purchase) + if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) { + + if (!purchase.isAcknowledged) { + val params = AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.purchaseToken).build() + this.billingClient.acknowledgePurchase(params) { result -> + Timber.d("Acknowledge result: ${result.responseCode}") + } + } + + this._isProUser = true + this.purchaseDelegate?.let { + Handler(Looper.getMainLooper()).post { + it.purchaseDidSucceed(purchase) + } + this.purchaseDelegate = null } - this.purchaseDelegate = null } + } else -> { } diff --git a/app/src/main/java/net/pokeranalytics/android/util/billing/Security.kt b/app/src/main/java/net/pokeranalytics/android/util/billing/Security.kt index 07676606..a8bb60ad 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/billing/Security.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/billing/Security.kt @@ -17,10 +17,9 @@ package net.pokeranalytics.android.util.billing */ - import android.text.TextUtils import android.util.Base64 -import com.android.billingclient.util.BillingHelper +import timber.log.Timber import java.io.IOException import java.security.* import java.security.spec.InvalidKeySpecException @@ -32,10 +31,8 @@ import java.security.spec.X509EncodedKeySpec */ object Security { - private val TAG = "IABUtil/Security" - - private val KEY_FACTORY_ALGORITHM = "RSA" - private val SIGNATURE_ALGORITHM = "SHA1withRSA" + private const val KEY_FACTORY_ALGORITHM = "RSA" + private const val SIGNATURE_ALGORITHM = "SHA1withRSA" /** * Verifies that the data was signed with the given signature, and returns the verified @@ -54,7 +51,7 @@ object Security { if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) || TextUtils.isEmpty(signature) ) { - BillingHelper.logWarn(TAG, "Purchase verification failed: missing data.") + Timber.w("Purchase verification failed: missing data.") return false } @@ -70,7 +67,7 @@ object Security { * is invalid */ @Throws(IOException::class) - fun generatePublicKey(encodedPublicKey: String): PublicKey { + private fun generatePublicKey(encodedPublicKey: String): PublicKey { try { val decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT) val keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM) @@ -80,7 +77,7 @@ object Security { throw RuntimeException(e) } catch (e: InvalidKeySpecException) { val msg = "Invalid key specification: $e" - BillingHelper.logWarn(TAG, msg) + Timber.w(msg) throw IOException(msg) } @@ -95,12 +92,12 @@ object Security { * @param signature server signature * @return true if the data and signature match */ - fun verify(publicKey: PublicKey, signedData: String, signature: String): Boolean { + private fun verify(publicKey: PublicKey, signedData: String, signature: String): Boolean { val signatureBytes: ByteArray try { signatureBytes = Base64.decode(signature, Base64.DEFAULT) } catch (e: IllegalArgumentException) { - BillingHelper.logWarn(TAG, "Base64 decoding failed.") + Timber.w("Base64 decoding failed.") return false } @@ -109,7 +106,7 @@ object Security { signatureAlgorithm.initVerify(publicKey) signatureAlgorithm.update(signedData.toByteArray()) if (!signatureAlgorithm.verify(signatureBytes)) { - BillingHelper.logWarn(TAG, "Signature verification failed.") + Timber.w("Signature verification failed.") return false } return true @@ -117,9 +114,9 @@ object Security { // "RSA" is guaranteed to be available. throw RuntimeException(e) } catch (e: InvalidKeyException) { - BillingHelper.logWarn(TAG, "Invalid key specification.") + Timber.w("Invalid key specification.") } catch (e: SignatureException) { - BillingHelper.logWarn(TAG, "Signature exception.") + Timber.w("Signature exception.") } return false diff --git a/app/src/main/java/net/pokeranalytics/android/util/csv/CSVImporter.kt b/app/src/main/java/net/pokeranalytics/android/util/csv/CSVImporter.kt index f924f944..86dcaba6 100644 --- a/app/src/main/java/net/pokeranalytics/android/util/csv/CSVImporter.kt +++ b/app/src/main/java/net/pokeranalytics/android/util/csv/CSVImporter.kt @@ -191,7 +191,7 @@ open class CSVImporter(istream: InputStream) { fun save(realm: Realm) { this.parser.close() -// realm.refresh() + realm.refresh() this.currentDescriptor?.save(realm) this.usedDescriptors.forEach { descriptor -> @@ -201,7 +201,7 @@ open class CSVImporter(istream: InputStream) { fun cancel(realm: Realm) { this.parser.close() -// realm.refresh() + realm.refresh() this.currentDescriptor?.cancel(realm) this.usedDescriptors.forEach { descriptor -> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 16bf01b6..185c7a4b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -806,5 +806,6 @@ We\'ll send you a notification when your file is available. Expect approximately one minute! Show villain cards Please save before sharing + It looks like there is an issue here. Please contact the support to get help.