Bug fixes

This commit is contained in:
Lucas Lima 2020-08-31 20:35:10 -03:00
parent b8542ba688
commit b978683b7e
No known key found for this signature in database
GPG key ID: 049CCC5A365B00D2
11 changed files with 67 additions and 332 deletions

View file

@ -116,21 +116,6 @@
</intent-filter>
</activity>
<activity
android:name="dev.lucasnlm.antimine.TvGameActivity"
android:configChanges="screenSize|orientation"
android:theme="@style/AppTheme.NoActionBar">
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN"/>-->
<!-- <category android:name="android.intent.category.LAUNCHER"/>-->
<!-- </intent-filter>-->
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LEANBACK_LAUNCHER" />-->
<!-- </intent-filter>-->
</activity>
<activity
android:name="dev.lucasnlm.antimine.text.TextActivity"
android:theme="@style/AppTheme">

View file

@ -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,11 +539,14 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
gameViewModel.victory()
refreshNewGameButton()
keepScreenOn(false)
if (!isResuming) {
waitAndShowEndGameDialog(
victory = true,
await = false
)
}
}
Event.GameOver -> {
val isResuming = (status == Status.PreGame)
val score = Score(
@ -555,6 +559,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
keepScreenOn(false)
gameViewModel.stopClock()
if (!isResuming) {
GlobalScope.launch(context = Dispatchers.Main) {
gameViewModel.gameOver(isResuming)
waitAndShowEndGameDialog(
@ -563,6 +568,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O
)
}
}
}
else -> { }
}
}

View file

@ -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<GameViewModel>()
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 -> { }
}
}
}

View file

@ -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<EndGameDialogViewModel>()
private val gameViewModel by sharedViewModel<GameViewModel>()
@ -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)
}
}
}
}
}

View file

@ -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()
}
}

View file

@ -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()

View file

@ -141,7 +141,7 @@ class GameViewModel(
levelSetup.postValue(setup)
refreshField()
eventObserver.postValue(Event.StartNewGame)
eventObserver.postValue(Event.ResumeGame)
analyticsManager.sentEvent(
Analytics.RetryGame(

View file

@ -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"
}

View file

@ -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)?) { }
}

View file

@ -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) {

View file

@ -24,13 +24,16 @@ class BillingManager(
}
private fun handlePurchases(purchases: List<Purchase>) {
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<Purchase> = 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<Purchase>?) {
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
val purchasesList: List<Purchase> = 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) {