Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
mustafaozhan committed Sep 12, 2021
2 parents bfbe1a0 + 8de2a35 commit afb0977
Show file tree
Hide file tree
Showing 36 changed files with 380 additions and 382 deletions.
3 changes: 2 additions & 1 deletion android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ dependencies {
implementation(NAVIGATION)
implementation(PLAY_CORE)
implementation(KOIN_ANDROID)
implementation(BILLING)
implementation(LIFECYCLE_RUNTIME)
coreLibraryDesugaring(DESUGARING)
debugImplementation(LEAK_CANARY)
Expand All @@ -151,5 +150,7 @@ dependencies {
implementation(project(BASE_MOB))
implementation(project(SCOPE_MOB))
implementation(project(LOG_MOB))

implementation(project(BILLING))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,25 @@
package com.github.mustafaozhan.ccc.android.app

import android.app.Application
import com.github.mustafaozhan.billing.BillingManager
import com.github.mustafaozhan.ccc.client.di.initAndroid
import com.github.mustafaozhan.logmob.initCrashlytics
import com.github.mustafaozhan.logmob.initLogger
import com.github.mustafaozhan.logmob.kermit
import org.koin.dsl.module

@Suppress("unused")
class CCCApplication : Application() {

override fun onCreate() {
super.onCreate()
initLogger()
initAndroid(this@CCCApplication)
initAndroid(
context = this@CCCApplication,
platformModule = module {
single { BillingManager(get()) }
}
)
kermit.d { "CCCApplication onCreate" }
initCrashlytics(this, enableAnalytics = true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,18 @@ import android.os.Bundle
import android.view.View
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.android.billingclient.api.AcknowledgePurchaseParams
import com.android.billingclient.api.AcknowledgePurchaseResponseListener
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.BillingClientStateListener
import com.android.billingclient.api.BillingFlowParams
import com.android.billingclient.api.BillingResult
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.PurchaseHistoryRecord
import com.android.billingclient.api.PurchaseHistoryResponseListener
import com.android.billingclient.api.PurchasesUpdatedListener
import com.android.billingclient.api.SkuDetails
import com.android.billingclient.api.SkuDetailsParams
import com.android.billingclient.api.SkuDetailsResponseListener
import com.github.mustafaozhan.basemob.bottomsheet.BaseVBBottomSheetDialogFragment
import com.github.mustafaozhan.billing.BillingEffect
import com.github.mustafaozhan.billing.BillingManager
import com.github.mustafaozhan.ccc.android.util.showDialog
import com.github.mustafaozhan.ccc.android.util.showLoading
import com.github.mustafaozhan.ccc.android.util.showSnack
import com.github.mustafaozhan.ccc.client.model.PurchaseHistory
import com.github.mustafaozhan.ccc.client.model.RemoveAdData
import com.github.mustafaozhan.ccc.android.util.toPurchaseHistoryList
import com.github.mustafaozhan.ccc.android.util.toRemoveAdDataList
import com.github.mustafaozhan.ccc.client.model.RemoveAdType
import com.github.mustafaozhan.ccc.client.viewmodel.adremove.AdRemoveEffect
import com.github.mustafaozhan.ccc.client.viewmodel.adremove.AdRemoveViewModel
import com.github.mustafaozhan.logmob.kermit
import com.github.mustafaozhan.scopemob.mapTo
import com.github.mustafaozhan.scopemob.whether
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.rewarded.RewardedAd
Expand All @@ -41,38 +28,34 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import mustafaozhan.github.com.mycurrencies.R
import mustafaozhan.github.com.mycurrencies.databinding.BottomSheetAdRemoveBinding
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel

@Suppress("TooManyFunctions")
class AdRemoveBottomSheet : BaseVBBottomSheetDialogFragment<BottomSheetAdRemoveBinding>(),
PurchaseHistoryResponseListener,
PurchasesUpdatedListener,
SkuDetailsResponseListener,
BillingClientStateListener,
AcknowledgePurchaseResponseListener {

private lateinit var billingClient: BillingClient
class AdRemoveBottomSheet : BaseVBBottomSheetDialogFragment<BottomSheetAdRemoveBinding>() {

private val billingManager: BillingManager by inject()
private val adRemoveViewModel: AdRemoveViewModel by viewModel()

private lateinit var removeAdsAdapter: RemoveAdsAdapter

private lateinit var skuDetails: List<SkuDetails>
private var acknowledgePurchaseParams: AcknowledgePurchaseParams? = null

override fun getViewBinding() = BottomSheetAdRemoveBinding.inflate(layoutInflater)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
kermit.d { "AdRemoveBottomSheet onViewCreated" }
billingManager.setupBillingClient(
viewLifecycleOwner.lifecycleScope,
RemoveAdType.getSkuList()
)
initViews()
observeStates()
observeEffect()
setupBillingClient()
observeEffects()
observeBillingEffects()
}

override fun onDestroyView() {
billingClient.endConnection()
billingManager.endConnection()
binding.recyclerViewBar.adapter = null
super.onDestroyView()
}
Expand All @@ -91,7 +74,7 @@ class AdRemoveBottomSheet : BaseVBBottomSheetDialogFragment<BottomSheetAdRemoveB
}
}.launchIn(viewLifecycleOwner.lifecycleScope)

private fun observeEffect() = adRemoveViewModel.effect
private fun observeEffects() = adRemoveViewModel.effect
.flowWithLifecycle(lifecycle)
.onEach { viewEffect ->
kermit.d { "AdRemoveBottomSheet observeEffect ${viewEffect::class.simpleName}" }
Expand All @@ -100,18 +83,17 @@ class AdRemoveBottomSheet : BaseVBBottomSheetDialogFragment<BottomSheetAdRemoveB
if (viewEffect.removeAdType == RemoveAdType.VIDEO) {
prepareRewardedAdFlow()
} else {
launchBillingFlow(viewEffect.removeAdType.data.skuId)
billingManager.launchBillingFlow(
requireActivity(),
viewEffect.removeAdType.data.skuId
)
}
}
is AdRemoveEffect.AdsRemoved -> {
if (viewEffect.removeAdType == RemoveAdType.VIDEO) {
restartActivity()
} else {
acknowledgePurchaseParams?.let {
billingClient.acknowledgePurchase(it, this)
} ?: run {
restartActivity()
}
billingManager.acknowledgePurchase()
}
}
AdRemoveEffect.AlreadyAdFree -> showSnack(
Expand All @@ -121,6 +103,24 @@ class AdRemoveBottomSheet : BaseVBBottomSheetDialogFragment<BottomSheetAdRemoveB
}
}.launchIn(viewLifecycleOwner.lifecycleScope)

private fun observeBillingEffects() = billingManager.effect
.flowWithLifecycle(lifecycle)
.onEach { viewEffect ->
kermit.d { "AdRemoveBottomSheet observeBillingEffects ${viewEffect::class.simpleName}" }
when (viewEffect) {
BillingEffect.SuccessfulPurchase -> restartActivity()
is BillingEffect.RestorePurchase -> adRemoveViewModel.restorePurchase(
viewEffect.purchaseHistoryRecordList.toPurchaseHistoryList()
)
is BillingEffect.AddInAppBillingMethods -> adRemoveViewModel.addInAppBillingMethods(
viewEffect.skuDetailsList.toRemoveAdDataList()
)
is BillingEffect.UpdateAddFreeDate -> adRemoveViewModel.updateAddFreeDate(
RemoveAdType.getBySku(viewEffect.sku)
)
}
}.launchIn(viewLifecycleOwner.lifecycleScope)

private fun prepareRewardedAdFlow() {
showDialog(
requireActivity(),
Expand All @@ -138,26 +138,6 @@ class AdRemoveBottomSheet : BaseVBBottomSheetDialogFragment<BottomSheetAdRemoveB
startActivity(intent)
}

private fun setupBillingClient() {
adRemoveViewModel.showLoadingView(true)
billingClient = BillingClient
.newBuilder(requireContext().applicationContext)
.enablePendingPurchases()
.setListener(this)
.build()
billingClient.startConnection(this)
}

private fun launchBillingFlow(skuId: String) = skuDetails
.firstOrNull { it.sku == skuId }
?.let {
val billingFlowParams = BillingFlowParams
.newBuilder()
.setSkuDetails(it)
.build()
billingClient.launchBillingFlow(requireActivity(), billingFlowParams)
}

private fun prepareRewardedAd() = context?.applicationContext?.let { applicationContext ->
RewardedAd.load(
applicationContext,
Expand All @@ -184,82 +164,4 @@ class AdRemoveBottomSheet : BaseVBBottomSheetDialogFragment<BottomSheetAdRemoveB
}
)
}

override fun onSkuDetailsResponse(
billingResult: BillingResult,
skuDetailsList: MutableList<SkuDetails>?
) {
kermit.d { "AdRemoveBottomSheet onSkuDetailsResponse ${billingResult.responseCode}" }

skuDetailsList?.whether {
billingResult.responseCode == BillingClient.BillingResponseCode.OK
}?.let { detailsList ->
skuDetails = detailsList
adRemoveViewModel.addInAppBillingMethods(detailsList.map {
RemoveAdData(it.price, it.description, it.sku)
})
} ?: run {
adRemoveViewModel.showLoadingView(false)
}
}

override fun onPurchaseHistoryResponse(
billingResult: BillingResult,
purchaseHistoryList: MutableList<PurchaseHistoryRecord>?
) {
kermit.d { "AdRemoveBottomSheet onPurchaseHistoryResponse ${billingResult.responseCode}" }

purchaseHistoryList?.mapNotNull { historyRecord ->
RemoveAdType.getBySku(historyRecord.skus.firstOrNull())?.let {
PurchaseHistory(historyRecord.purchaseTime, it)
}
}?.let { adRemoveViewModel.restorePurchase(it) }
}

override fun onPurchasesUpdated(
billingResult: BillingResult,
purchaseList: MutableList<Purchase>?
) {
kermit.d { "AdRemoveBottomSheet onPurchasesUpdated ${billingResult.responseCode}" }

purchaseList?.firstOrNull()
?.also {
acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(it.purchaseToken)
.build()
}?.mapTo { RemoveAdType.getBySku(skus.firstOrNull()) }
?.let {
adRemoveViewModel.updateAddFreeDate(it)
}
}

override fun onBillingSetupFinished(billingResult: BillingResult) {
kermit.d { "AdRemoveBottomSheet onBillingSetupFinished ${billingResult.responseCode}" }

adRemoveViewModel.showLoadingView(false)
billingClient.queryPurchaseHistoryAsync(BillingClient.SkuType.INAPP, this)

billingClient.whether(
{ isReady },
{ billingResult.responseCode == BillingClient.BillingResponseCode.OK }
)?.apply {
val skuDetailsParams = SkuDetailsParams.newBuilder()
.setSkusList(RemoveAdType.getSkuList())
.setType(BillingClient.SkuType.INAPP)
.build()
querySkuDetailsAsync(skuDetailsParams, this@AdRemoveBottomSheet)
}
}

override fun onBillingServiceDisconnected() {
kermit.d { "AdRemoveBottomSheet onBillingServiceDisconnected" }
adRemoveViewModel.showLoadingView(false)
}

override fun onAcknowledgePurchaseResponse(billingResult: BillingResult) {
kermit.d { "AdRemoveBottomSheet onAcknowledgePurchaseResponse" }
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
restartActivity()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class BarBottomSheet :
kermit.d { "BarBottomSheet onViewCreated" }
initViews()
observeStates()
observeEffect()
observeEffects()
setListeners()
}

Expand Down Expand Up @@ -65,7 +65,7 @@ class BarBottomSheet :
}
}.launchIn(viewLifecycleOwner.lifecycleScope)

private fun observeEffect() = barViewModel.effect
private fun observeEffects() = barViewModel.effect
.flowWithLifecycle(lifecycle)
.onEach { viewEffect ->
kermit.d { "BarBottomSheet observeEffect ${viewEffect::class.simpleName}" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class CalculatorFragment : BaseVBFragment<FragmentCalculatorBinding>() {
initViews()
observeNavigationResults()
observeStates()
observeEffect()
observeEffects()
setListeners()
}

Expand Down Expand Up @@ -85,7 +85,7 @@ class CalculatorFragment : BaseVBFragment<FragmentCalculatorBinding>() {
}
}.launchIn(viewLifecycleOwner.lifecycleScope)

private fun observeEffect() = calculatorViewModel.effect
private fun observeEffects() = calculatorViewModel.effect
.flowWithLifecycle(lifecycle)
.onEach { viewEffect ->
kermit.d { "CalculatorFragment observeEffect ${viewEffect::class.simpleName}" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class CurrenciesFragment : BaseVBFragment<FragmentCurrenciesBinding>() {
kermit.d { "CurrenciesFragment onViewCreated" }
initViews()
observeStates()
observeEffect()
observeEffects()
setListeners()
}

Expand Down Expand Up @@ -97,7 +97,7 @@ class CurrenciesFragment : BaseVBFragment<FragmentCurrenciesBinding>() {
}
}.launchIn(viewLifecycleOwner.lifecycleScope)

private fun observeEffect() = currenciesViewModel.effect
private fun observeEffects() = currenciesViewModel.effect
.flowWithLifecycle(lifecycle)
.onEach { viewEffect ->
kermit.d { "CurrenciesFragment observeEffect ${viewEffect::class.simpleName}" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ class MainActivity : BaseActivity() {
AppCompatDelegate.setDefaultNightMode(mainViewModel.getAppTheme())
setContentView(R.layout.activity_main)
checkDestination()
observeEffect()
observeEffects()
mainViewModel.checkReview()
}

private fun observeEffect() = mainViewModel.effect
private fun observeEffects() = mainViewModel.effect
.flowWithLifecycle(lifecycle)
.onEach { viewEffect ->
kermit.d { "MainActivity observeEffect ${viewEffect::class.simpleName}" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class SettingsFragment : BaseVBFragment<FragmentSettingsBinding>() {
kermit.d { "SettingsFragment onViewCreated" }
initViews()
observeStates()
observeEffect()
observeEffects()
setListeners()
}

Expand Down Expand Up @@ -123,7 +123,7 @@ class SettingsFragment : BaseVBFragment<FragmentSettingsBinding>() {
}
}.launchIn(viewLifecycleOwner.lifecycleScope)

private fun observeEffect() = settingsViewModel.effect
private fun observeEffects() = settingsViewModel.effect
.flowWithLifecycle(lifecycle)
.onEach { viewEffect ->
kermit.d { "SettingsFragment observeEffect ${viewEffect::class.simpleName}" }
Expand Down
Loading

0 comments on commit afb0977

Please sign in to comment.