diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e58b9851..e4ac08d6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -116,21 +116,6 @@ - - - - - - - - - - - - diff --git a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt index 3e41e680..0b926159 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt @@ -527,6 +527,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O keepScreenOn(true) } Event.Victory -> { + val isResuming = (status == Status.PreGame) val score = Score( rightMines, totalMines, @@ -538,10 +539,13 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O gameViewModel.victory() refreshNewGameButton() keepScreenOn(false) - waitAndShowEndGameDialog( - victory = true, - await = false - ) + + if (!isResuming) { + waitAndShowEndGameDialog( + victory = true, + await = false + ) + } } Event.GameOver -> { val isResuming = (status == Status.PreGame) @@ -555,12 +559,14 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O keepScreenOn(false) gameViewModel.stopClock() - GlobalScope.launch(context = Dispatchers.Main) { - gameViewModel.gameOver(isResuming) - waitAndShowEndGameDialog( - victory = false, - await = true - ) + if (!isResuming) { + GlobalScope.launch(context = Dispatchers.Main) { + gameViewModel.gameOver(isResuming) + waitAndShowEndGameDialog( + victory = false, + await = true + ) + } } } else -> { } diff --git a/app/src/main/java/dev/lucasnlm/antimine/TvGameActivity.kt b/app/src/main/java/dev/lucasnlm/antimine/TvGameActivity.kt deleted file mode 100644 index 9a11a4e4..00000000 --- a/app/src/main/java/dev/lucasnlm/antimine/TvGameActivity.kt +++ /dev/null @@ -1,279 +0,0 @@ -package dev.lucasnlm.antimine - -import android.content.Intent -import android.os.Bundle -import android.os.Handler -import android.text.format.DateUtils -import android.view.View -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatActivity -import androidx.core.os.HandlerCompat -import androidx.fragment.app.FragmentTransaction -import androidx.preference.PreferenceManager -import dev.lucasnlm.antimine.about.AboutActivity -import dev.lucasnlm.antimine.common.level.models.Difficulty -import dev.lucasnlm.antimine.common.level.models.Event -import dev.lucasnlm.antimine.common.level.models.Score -import dev.lucasnlm.antimine.common.level.models.Status -import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel -import dev.lucasnlm.antimine.custom.CustomLevelDialogFragment -import dev.lucasnlm.antimine.level.view.LevelFragment -import dev.lucasnlm.antimine.preferences.PreferencesActivity -import kotlinx.android.synthetic.main.activity_tv_game.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.koin.androidx.viewmodel.ext.android.viewModel - -class TvGameActivity : AppCompatActivity() { - private val gameViewModel by viewModel() - - private var status: Status = Status.PreGame - private var totalMines: Int = 0 - private var totalArea: Int = 0 - private var rightMines: Int = 0 - private var currentTime: Long = 0 - - private var keepConfirmingNewGame = true - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_tv_game) - bindViewModel() - - PreferenceManager.setDefaultValues(this, R.xml.preferences, false) - - loadGameFragment() - } - - private fun bindViewModel() = gameViewModel.apply { - eventObserver.observe( - this@TvGameActivity, - { - onGameEvent(it) - } - ) - - elapsedTimeSeconds.observe( - this@TvGameActivity, - { - timer.apply { - visibility = if (it == 0L) View.GONE else View.VISIBLE - text = DateUtils.formatElapsedTime(it) - } - currentTime = it - } - ) - - mineCount.observe( - this@TvGameActivity, - { - minesCount.apply { - visibility = View.VISIBLE - text = it.toString() - } - } - ) - - difficulty.observe( - this@TvGameActivity, - { - // onChangeDifficulty(it) - } - ) - - field.observe( - this@TvGameActivity, - { area -> - val mines = area.filter { it.hasMine } - totalArea = area.count() - totalMines = mines.count() - rightMines = mines.map { if (it.mark.isFlag()) 1 else 0 }.sum() - } - ) - } - - override fun onResume() { - super.onResume() - if (status == Status.Running) { - gameViewModel.resumeGame() - } - } - - override fun onPause() { - super.onPause() - - if (status == Status.Running) { - gameViewModel.pauseGame() - } - } - - private fun loadGameFragment() { - val fragmentManager = supportFragmentManager - - fragmentManager.popBackStack() - - fragmentManager.findFragmentById(R.id.levelContainer)?.let { it -> - fragmentManager.beginTransaction().apply { - remove(it) - commitAllowingStateLoss() - } - } - - fragmentManager.beginTransaction().apply { - replace(R.id.levelContainer, LevelFragment()) - setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) - commitAllowingStateLoss() - } - } - - private fun newGameConfirmation(action: () -> Unit) { - AlertDialog.Builder(this).apply { - setTitle(R.string.new_game) - setMessage(R.string.retry_sure) - setPositiveButton(R.string.resume) { _, _ -> action() } - setNegativeButton(R.string.cancel, null) - show() - } - } - - private fun showCustomLevelDialog() { - CustomLevelDialogFragment().apply { - show(supportFragmentManager, "custom_level_fragment") - } - } - - private fun showAbout() { - Intent(this, AboutActivity::class.java).apply { - startActivity(this) - } - } - - private fun showSettings() { - Intent(this, PreferencesActivity::class.java).apply { - startActivity(this) - } - } - - private fun showVictory() { - AlertDialog.Builder(this).apply { - setTitle(R.string.you_won) - setMessage(R.string.all_mines_disabled) - setCancelable(false) - setPositiveButton(R.string.new_game) { _, _ -> - GlobalScope.launch { - gameViewModel.startNewGame() - } - } - setNegativeButton(R.string.cancel, null) - show() - } - } - - private fun waitAndShowConfirmNewGame() { - if (keepConfirmingNewGame) { - HandlerCompat.postDelayed( - Handler(), - { - if (status is Status.Over && !isFinishing) { - AlertDialog.Builder(this).apply { - setTitle(R.string.new_game) - setMessage(R.string.new_game_request) - setPositiveButton(R.string.yes) { _, _ -> - GlobalScope.launch { - gameViewModel.startNewGame() - } - } - setNegativeButton(R.string.cancel, null) - }.show() - - keepConfirmingNewGame = false - } - }, - null, - DateUtils.SECOND_IN_MILLIS - ) - } - } - - private fun waitAndShowGameOverConfirmNewGame() { - HandlerCompat.postDelayed( - Handler(), - { - if (status is Status.Over && !isFinishing) { - AlertDialog.Builder(this).apply { - setTitle(R.string.you_lost) - setMessage(R.string.new_game_request) - setPositiveButton(R.string.yes) { _, _ -> - GlobalScope.launch { - gameViewModel.startNewGame() - } - } - setNegativeButton(R.string.cancel, null) - }.show() - } - }, - null, - DateUtils.SECOND_IN_MILLIS - ) - } - - private fun changeDifficulty(newDifficulty: Difficulty) { - if (status == Status.PreGame) { - GlobalScope.launch { - gameViewModel.startNewGame(newDifficulty) - } - } else { - newGameConfirmation { - GlobalScope.launch { - gameViewModel.startNewGame(newDifficulty) - } - } - } - } - - private fun onGameEvent(event: Event) { - when (event) { - Event.ResumeGame -> { - invalidateOptionsMenu() - } - Event.StartNewGame -> { - status = Status.PreGame - invalidateOptionsMenu() - } - Event.Resume, Event.Running -> { - status = Status.Running - gameViewModel.runClock() - invalidateOptionsMenu() - } - Event.Victory -> { - val score = Score( - rightMines, - totalMines, - totalArea - ) - status = Status.Over(currentTime, score) - gameViewModel.stopClock() - gameViewModel.revealAllEmptyAreas() - invalidateOptionsMenu() - showVictory() - } - Event.GameOver -> { - val score = Score( - rightMines, - totalMines, - totalArea - ) - status = Status.Over(currentTime, score) - invalidateOptionsMenu() - gameViewModel.stopClock() - - GlobalScope.launch(context = Dispatchers.Main) { - gameViewModel.gameOver(false) - waitAndShowGameOverConfirmNewGame() - } - } - else -> { } - } - } -} diff --git a/app/src/main/java/dev/lucasnlm/antimine/level/view/EndGameDialogFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/level/view/EndGameDialogFragment.kt index 45ed5f53..dbe95e58 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/level/view/EndGameDialogFragment.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/level/view/EndGameDialogFragment.kt @@ -12,8 +12,11 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.lifecycleScope import dev.lucasnlm.antimine.R import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel +import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository import dev.lucasnlm.antimine.level.viewmodel.EndGameDialogEvent import dev.lucasnlm.antimine.level.viewmodel.EndGameDialogViewModel +import dev.lucasnlm.external.Ads +import dev.lucasnlm.external.IAdsManager import dev.lucasnlm.external.IInstantAppManager import kotlinx.coroutines.flow.collect import org.koin.android.ext.android.inject @@ -22,6 +25,8 @@ import org.koin.androidx.viewmodel.ext.android.viewModel class EndGameDialogFragment : AppCompatDialogFragment() { private val instantAppManager: IInstantAppManager by inject() + private val adsManager: IAdsManager by inject() + private val preferencesRepository: IPreferencesRepository by inject() private val endGameViewModel by viewModel() private val gameViewModel by sharedViewModel() @@ -76,6 +81,12 @@ class EndGameDialogFragment : AppCompatDialogFragment() { } else { setNeutralButton(R.string.retry) { _, _ -> gameViewModel.retryObserver.postValue(Unit) + + activity?.let { + if (!preferencesRepository.isPremiumEnabled()) { + adsManager.requestRewarded(it, Ads.RewardsAds) + } + } } } } diff --git a/app/src/main/java/dev/lucasnlm/antimine/splash/SplashActivity.kt b/app/src/main/java/dev/lucasnlm/antimine/splash/SplashActivity.kt index bd56b95c..02d80c8e 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/splash/SplashActivity.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/splash/SplashActivity.kt @@ -1,24 +1,23 @@ package dev.lucasnlm.antimine.splash -import android.app.UiModeManager import android.content.Intent -import android.content.res.Configuration import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import dev.lucasnlm.antimine.GameActivity -import dev.lucasnlm.antimine.TvGameActivity +import dev.lucasnlm.antimine.support.IapHandler +import dev.lucasnlm.external.IBillingManager +import org.koin.android.ext.android.inject class SplashActivity : AppCompatActivity() { + private val billingManager: IBillingManager by inject() + private val iapHandler: IapHandler by inject() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val uiModeManager: UiModeManager = getSystemService(UI_MODE_SERVICE) as UiModeManager - if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) { - Intent(this, TvGameActivity::class.java).run { startActivity(this) } - } else { - Intent(this, GameActivity::class.java).run { startActivity(this) } - } + billingManager.start(iapHandler) + Intent(this, GameActivity::class.java).run { startActivity(this) } finish() } } diff --git a/app/src/main/java/dev/lucasnlm/antimine/support/SupportAppDialogFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/support/SupportAppDialogFragment.kt index 165f3de3..b8c0d60d 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/support/SupportAppDialogFragment.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/support/SupportAppDialogFragment.kt @@ -9,6 +9,7 @@ import dev.lucasnlm.antimine.R import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager import dev.lucasnlm.antimine.core.analytics.models.Analytics import dev.lucasnlm.antimine.core.themes.repository.IThemeRepository +import dev.lucasnlm.external.Ads import dev.lucasnlm.external.IAdsManager import dev.lucasnlm.external.IBillingManager import kotlinx.coroutines.launch @@ -43,7 +44,7 @@ class SupportAppDialogFragment : AppCompatDialogFragment() { if (showUnlockMessage) { setNeutralButton(R.string.try_it) { _, _ -> analyticsManager.sentEvent(Analytics.UnlockRewardedDialog) - adsManager.requestRewarded(requireActivity(), "ca-app-pub-3940256099942544/5224354917") { + adsManager.requestRewarded(requireActivity(), Ads.RewardsAds) { if (targetThemeId > 0) { themeRepository.setTheme(targetThemeId) requireActivity().recreate() diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt index bd401d81..350b0d66 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt @@ -141,7 +141,7 @@ class GameViewModel( levelSetup.postValue(setup) refreshField() - eventObserver.postValue(Event.StartNewGame) + eventObserver.postValue(Event.ResumeGame) analyticsManager.sentEvent( Analytics.RetryGame( diff --git a/external/src/main/java/dev/lucasnlm/external/IAdsManager.kt b/external/src/main/java/dev/lucasnlm/external/IAdsManager.kt index 9e7201f1..ec6f5695 100644 --- a/external/src/main/java/dev/lucasnlm/external/IAdsManager.kt +++ b/external/src/main/java/dev/lucasnlm/external/IAdsManager.kt @@ -6,5 +6,9 @@ import android.content.Context interface IAdsManager { fun start(context: Context) fun isReady(): Boolean - fun requestRewarded(activity: Activity, adUnitId: String, onRewarded: () -> Unit) + fun requestRewarded(activity: Activity, adUnitId: String, onRewarded: (() -> Unit)? = null) +} + +object Ads { + const val RewardsAds = "ca-app-pub-3940256099942544/5224354917" } diff --git a/foss/src/main/java/dev/lucasnlm/external/AdsManager.kt b/foss/src/main/java/dev/lucasnlm/external/AdsManager.kt index e77b8301..4989bcb1 100644 --- a/foss/src/main/java/dev/lucasnlm/external/AdsManager.kt +++ b/foss/src/main/java/dev/lucasnlm/external/AdsManager.kt @@ -8,5 +8,5 @@ class AdsManager : IAdsManager { override fun isReady(): Boolean = false - override fun requestRewarded(activity: Activity, adUnitId: String, onRewarded: () -> Unit) { } + override fun requestRewarded(activity: Activity, adUnitId: String, onRewarded: (() -> Unit)?) { } } diff --git a/proprietary/src/main/java/dev/lucasnlm/external/AdsManager.kt b/proprietary/src/main/java/dev/lucasnlm/external/AdsManager.kt index f4a523a5..16c55ba4 100644 --- a/proprietary/src/main/java/dev/lucasnlm/external/AdsManager.kt +++ b/proprietary/src/main/java/dev/lucasnlm/external/AdsManager.kt @@ -14,6 +14,7 @@ import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback class AdsManager : IAdsManager { private var unlockTheme: RewardedAd? = null + private val rewardedAdId = Ads.RewardsAds override fun start(context: Context) { MobileAds.initialize(context) { @@ -22,7 +23,7 @@ class AdsManager : IAdsManager { } private fun loadRewardedAd(context: Context): RewardedAd { - return RewardedAd(context, "ca-app-pub-3940256099942544/5224354917").apply { + return RewardedAd(context, rewardedAdId).apply { val adLoadCallback = object : RewardedAdLoadCallback() { override fun onRewardedAdLoaded() { // Loaded @@ -41,7 +42,7 @@ class AdsManager : IAdsManager { return unlockTheme != null } - override fun requestRewarded(activity: Activity, adUnitId: String, onRewarded: () -> Unit) { + override fun requestRewarded(activity: Activity, adUnitId: String, onRewarded: (() -> Unit)?) { if (isReady()) { val context = activity.applicationContext @@ -56,7 +57,7 @@ class AdsManager : IAdsManager { } override fun onUserEarnedReward(@NonNull reward: RewardItem) { - onRewarded() + onRewarded?.invoke() } override fun onRewardedAdFailedToShow(adError: AdError) { diff --git a/proprietary/src/main/java/dev/lucasnlm/external/BillingManager.kt b/proprietary/src/main/java/dev/lucasnlm/external/BillingManager.kt index 2ca884a0..099d8630 100644 --- a/proprietary/src/main/java/dev/lucasnlm/external/BillingManager.kt +++ b/proprietary/src/main/java/dev/lucasnlm/external/BillingManager.kt @@ -24,13 +24,16 @@ class BillingManager( } private fun handlePurchases(purchases: List) { - purchases.firstOrNull { + val status: Boolean = purchases.firstOrNull { it.sku == BASIC_SUPPORT - }?.let { - if (it.purchaseState == Purchase.PurchaseState.PURCHASED) { - unlockAppListener?.onLockStatusChanged(isFreeUnlock = false, status = true) + }.let { + when (it?.purchaseState) { + Purchase.PurchaseState.PURCHASED, Purchase.PurchaseState.PENDING -> true + else -> false } } + + unlockAppListener?.onLockStatusChanged(isFreeUnlock = false, status = status) } override fun onBillingServiceDisconnected() { @@ -39,24 +42,28 @@ class BillingManager( } override fun onBillingSetupFinished(billingResult: BillingResult) { - if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { + val purchasesList: List = if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { // The BillingClient is ready. You can query purchases here. - billingClient.queryPurchases(BillingClient.SkuType.INAPP).purchasesList?.let { - handlePurchases(it.toList()) + billingClient.queryPurchases(BillingClient.SkuType.INAPP).purchasesList.let { + it?.toList() ?: listOf() } + } else { + listOf() } + + handlePurchases(purchasesList) } override fun onPurchasesUpdated(billingResult: BillingResult, purchases: MutableList?) { - if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { + val purchasesList: List = if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { // The BillingClient is ready. You can query purchases here. - purchases?.let { - handlePurchases(it.toList()) - } + purchases?.toList() ?: listOf() } else { - unlockAppListener?.onLockStatusChanged(status = false) + listOf() } + + handlePurchases(purchasesList) } override fun start(unlockAppListener: UnlockAppListener) {