Add new GameOver and Victory dialogs

This commit is contained in:
Lucas Lima 2020-03-07 11:10:02 -03:00
parent a662044850
commit f84dc2179d
11 changed files with 155 additions and 173 deletions

View file

@ -31,6 +31,7 @@ import dagger.android.support.DaggerAppCompatActivity
import dev.lucasnlm.antimine.common.level.data.DifficultyPreset
import dev.lucasnlm.antimine.common.level.data.GameEvent
import dev.lucasnlm.antimine.common.level.data.GameStatus
import dev.lucasnlm.antimine.common.level.data.Mark
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModelFactory
@ -39,7 +40,7 @@ import dev.lucasnlm.antimine.core.analytics.Event
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.core.utils.isDarkModeEnabled
import dev.lucasnlm.antimine.level.view.CustomLevelDialogFragment
import dev.lucasnlm.antimine.level.view.GameOverDialogFragment
import dev.lucasnlm.antimine.level.view.EndGameDialogFragment
import dev.lucasnlm.antimine.level.view.LevelFragment
import kotlinx.android.synthetic.main.activity_game.*
import kotlinx.coroutines.GlobalScope
@ -60,8 +61,10 @@ class GameActivity : DaggerAppCompatActivity() {
private lateinit var viewModel: GameViewModel
private var gameStatus: GameStatus = GameStatus.PreGame
private var keepConfirmingNewGame = true
private val usingLargeArea by lazy { preferencesRepository.useLargeAreas() }
private var totalMines: Int = 0
private var rightMines: Int = 0
private var currentTime: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -86,21 +89,31 @@ class GameActivity : DaggerAppCompatActivity() {
eventObserver.observe(this@GameActivity, Observer {
onGameEvent(it)
})
elapsedTimeSeconds.observe(this@GameActivity, Observer {
timer.apply {
visibility = if (it == 0L) View.GONE else View.VISIBLE
text = DateUtils.formatElapsedTime(it)
}
currentTime = it
})
mineCount.observe(this@GameActivity, Observer {
minesCount.apply {
visibility = View.VISIBLE
text = it.toString()
}
})
difficulty.observe(this@GameActivity, Observer {
onChangeDifficulty(it)
})
field.observe(this@GameActivity, Observer { area ->
val mines = area.filter { it.hasMine }
totalMines = mines.count()
rightMines = mines.map { if (it.mark == Mark.Flag) 1 else 0 }.sum()
})
}
override fun onBackPressed() {
@ -138,7 +151,7 @@ class GameActivity : DaggerAppCompatActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean =
when (gameStatus) {
GameStatus.Over, GameStatus.Running -> {
is GameStatus.Over, is GameStatus.Running -> {
menuInflater.inflate(R.menu.top_menu_over, menu)
true
}
@ -180,9 +193,16 @@ class GameActivity : DaggerAppCompatActivity() {
drawer.apply {
addDrawerListener(
ActionBarDrawerToggle(this@GameActivity, drawer, toolbar, R.string.open_menu, R.string.close_menu).apply {
ActionBarDrawerToggle(
this@GameActivity,
drawer,
toolbar,
R.string.open_menu,
R.string.close_menu
).apply {
if (!isDarkModeEnabled(applicationContext)) {
drawerArrowDrawable.color = ContextCompat.getColor(applicationContext, R.color.primary)
drawerArrowDrawable.color =
ContextCompat.getColor(applicationContext, R.color.primary)
}
syncState()
@ -195,14 +215,14 @@ class GameActivity : DaggerAppCompatActivity() {
}
override fun onDrawerOpened(drawerView: View) {
if (gameStatus != GameStatus.Over) {
if (gameStatus is GameStatus.Over) {
viewModel.pauseGame()
}
analyticsManager.sentEvent(Event.OpenDrawer())
}
override fun onDrawerClosed(drawerView: View) {
if (gameStatus != GameStatus.Over) {
if (gameStatus is GameStatus.Over) {
viewModel.resumeGame()
}
analyticsManager.sentEvent(Event.CloseDrawer())
@ -343,65 +363,32 @@ class GameActivity : DaggerAppCompatActivity() {
}
}
private fun showVictory() {
AlertDialog.Builder(this, R.style.MyDialog).apply {
setTitle(R.string.you_won)
setMessage(R.string.all_mines_disabled)
setCancelable(false)
setPositiveButton(R.string.new_game) { _, _ ->
GlobalScope.launch {
viewModel.startNewGame()
}
}
setNegativeButton(R.string.cancel, null)
show()
}
}
private fun waitAndShowConfirmNewGame() {
if (keepConfirmingNewGame) {
postDelayed(Handler(), {
if (this.gameStatus == GameStatus.Over && !isFinishing) {
GameOverDialogFragment().apply {
setOnNewGameClicked {
GlobalScope.launch {
viewModel.startNewGame()
}
}
show(supportFragmentManager, "custom_level_fragment")
}
// AlertDialog.Builder(this, R.style.MyDialog).apply {
// setTitle(R.string.new_game)
// setMessage(R.string.new_game_request)
// setPositiveButton(R.string.yes) { _, _ ->
// GlobalScope.launch {
// viewModel.startNewGame()
// }
// }
// setNegativeButton(R.string.cancel, null)
// }.show()
keepConfirmingNewGame = false
}
}, null, DateUtils.SECOND_IN_MILLIS)
}
}
private fun waitAndShowGameOverConfirmNewGame() {
private fun waitAndShowEndGameDialog(victory: Boolean, await: Long = DateUtils.SECOND_IN_MILLIS) {
postDelayed(Handler(), {
if (this.gameStatus == GameStatus.Over && !isFinishing) {
AlertDialog.Builder(this, R.style.MyDialog).apply {
setTitle(R.string.you_lost)
setMessage(R.string.new_game_request)
setPositiveButton(R.string.yes) { _, _ ->
GlobalScope.launch {
viewModel.startNewGame()
}
if (gameStatus is GameStatus.Over && !isFinishing) {
val over = gameStatus as GameStatus.Over
val message: String = when {
victory -> {
getString(R.string.game_over_desc_4, over.time)
}
setNegativeButton(R.string.cancel, null)
}.show()
over.rightMines/over.totalMines > 0.9 -> {
getString(R.string.game_over_desc_3)
}
over.rightMines < 4 -> {
getString(arrayOf(R.string.game_over_desc_0,R.string.game_over_desc_1).random())
}
else -> {
getString(R.string.game_over_desc_2, over.rightMines, over.totalMines, over.time)
}
}
EndGameDialogFragment.newInstance(message, victory).apply {
show(supportFragmentManager, "custom_level_fragment")
}
}
}, null, DateUtils.SECOND_IN_MILLIS)
}, null, await)
}
private fun changeDifficulty(newDifficulty: DifficultyPreset) {
@ -433,27 +420,34 @@ class GameActivity : DaggerAppCompatActivity() {
invalidateOptionsMenu()
}
GameEvent.Victory -> {
gameStatus = GameStatus.Over
gameStatus = GameStatus.Over(rightMines, totalMines, currentTime)
viewModel.stopClock()
viewModel.revealAllEmptyAreas()
viewModel.victory()
invalidateOptionsMenu()
showVictory()
waitAndShowEndGameDialog(true, 0L)
}
GameEvent.GameOver -> {
gameStatus = GameStatus.Over
gameStatus = GameStatus.Over(rightMines, totalMines, currentTime)
invalidateOptionsMenu()
viewModel.stopClock()
viewModel.gameOver()
waitAndShowGameOverConfirmNewGame()
waitAndShowEndGameDialog(false)
}
GameEvent.ResumeVictory, GameEvent.ResumeGameOver -> {
gameStatus = GameStatus.Over
GameEvent.ResumeVictory -> {
gameStatus = GameStatus.Over(rightMines, totalMines, currentTime)
invalidateOptionsMenu()
viewModel.stopClock()
waitAndShowConfirmNewGame()
waitAndShowEndGameDialog(true)
}
GameEvent.ResumeGameOver -> {
gameStatus = GameStatus.Over(rightMines, totalMines, currentTime)
invalidateOptionsMenu()
viewModel.stopClock()
waitAndShowEndGameDialog(false)
}
else -> {
@ -470,10 +464,12 @@ class GameActivity : DaggerAppCompatActivity() {
appUpdateInfoTask.addOnSuccessListener { info ->
if (info.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
&& info.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
) {
try {
appUpdateManager.startUpdateFlowForResult(
info, AppUpdateType.FLEXIBLE, this, 1)
info, AppUpdateType.FLEXIBLE, this, 1
)
} catch (e: IntentSender.SendIntentException) {
Log.e(TAG, "Fail to request update.")
}
@ -497,7 +493,12 @@ class GameActivity : DaggerAppCompatActivity() {
try {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName")))
} catch (e: ActivityNotFoundException) {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$packageName")))
startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
)
)
}
analyticsManager.sentEvent(Event.TapRatingRequest(from))

View file

@ -102,7 +102,7 @@ class TvGameActivity : DaggerAppCompatActivity() {
override fun onCreateOptionsMenu(menu: Menu): Boolean =
when (gameStatus) {
GameStatus.Over, GameStatus.Running -> {
is GameStatus.Over, is GameStatus.Running -> {
menuInflater.inflate(R.menu.top_menu_over, menu)
true
}
@ -202,7 +202,7 @@ class TvGameActivity : DaggerAppCompatActivity() {
private fun waitAndShowConfirmNewGame() {
if (keepConfirmingNewGame) {
HandlerCompat.postDelayed(Handler(), {
if (this.gameStatus == GameStatus.Over && !isFinishing) {
if (gameStatus is GameStatus.Over && !isFinishing) {
AlertDialog.Builder(this, R.style.MyDialog).apply {
setTitle(R.string.new_game)
setMessage(R.string.new_game_request)
@ -222,7 +222,7 @@ class TvGameActivity : DaggerAppCompatActivity() {
private fun waitAndShowGameOverConfirmNewGame() {
HandlerCompat.postDelayed(Handler(), {
if (this.gameStatus == GameStatus.Over && !isFinishing) {
if (gameStatus is GameStatus.Over && !isFinishing) {
AlertDialog.Builder(this, R.style.MyDialog).apply {
setTitle(R.string.you_lost)
setMessage(R.string.new_game_request)
@ -266,14 +266,14 @@ class TvGameActivity : DaggerAppCompatActivity() {
invalidateOptionsMenu()
}
GameEvent.Victory -> {
gameStatus = GameStatus.Over
gameStatus = GameStatus.Over()
viewModel.stopClock()
viewModel.revealAllEmptyAreas()
invalidateOptionsMenu()
showVictory()
}
GameEvent.GameOver -> {
gameStatus = GameStatus.Over
gameStatus = GameStatus.Over()
invalidateOptionsMenu()
viewModel.stopClock()
viewModel.gameOver()
@ -281,7 +281,7 @@ class TvGameActivity : DaggerAppCompatActivity() {
waitAndShowGameOverConfirmNewGame()
}
GameEvent.ResumeVictory, GameEvent.ResumeGameOver -> {
gameStatus = GameStatus.Over
gameStatus = GameStatus.Over()
invalidateOptionsMenu()
viewModel.stopClock()

View file

@ -4,7 +4,7 @@ import dev.lucasnlm.antimine.level.view.CustomLevelDialogFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector
import dev.lucasnlm.antimine.core.scope.ActivityScope
import dev.lucasnlm.antimine.level.view.GameOverDialogFragment
import dev.lucasnlm.antimine.level.view.EndGameDialogFragment
import dev.lucasnlm.antimine.level.view.LevelFragment
@Module
@ -19,5 +19,5 @@ interface FragmentModule {
@ActivityScope
@ContributesAndroidInjector
fun contributeGameOverDialogFragmentInjector(): GameOverDialogFragment
fun contributeGameOverDialogFragmentInjector(): EndGameDialogFragment
}

View file

@ -15,7 +15,15 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import javax.inject.Inject
class GameOverDialogFragment : DaggerAppCompatDialogFragment() {
class EndGameDialogFragment : DaggerAppCompatDialogFragment() {
private val gameOverEmojis = listOf(
"\uD83D\uDE10","\uD83D\uDE44", "\uD83D\uDE25", "\uD83D\uDE13", "\uD83D\uDE31",
"\uD83E\uDD2C", "\uD83E\uDD15", "\uD83D\uDE16", "\uD83D\uDCA3", "\uD83D\uDE05")
private val victoryEmojis = listOf(
"\uD83D\uDE00", "\uD83D\uDE0E", "\uD83D\uDE1D", "\uD83E\uDD73", "\uD83D\uDE06")
@Inject
lateinit var viewModelFactory: GameViewModelFactory
@ -31,19 +39,37 @@ class GameOverDialogFragment : DaggerAppCompatDialogFragment() {
@SuppressLint("InflateParams")
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val isVictory = arguments?.getBoolean(DIALOG_STATE) == true
return AlertDialog.Builder(context!!, R.style.MyDialog).apply {
val view = LayoutInflater.from(context).inflate(R.layout.dialog_lose_game, null)
view.findViewById<TextView>(R.id.title).text = context.getString(R.string.game_over)
view.findViewById<TextView>(R.id.subtitle).text = "huehue"
val view = LayoutInflater.from(context).inflate(R.layout.dialog_end_game, null, false)
val emojis = if (isVictory) victoryEmojis else gameOverEmojis
val titleRes = if (isVictory) R.string.you_won else R.string.you_lost
view.findViewById<TextView>(R.id.title_emoji).text = emojis.random()
view.findViewById<TextView>(R.id.title).text = context.getString(titleRes)
view.findViewById<TextView>(R.id.subtitle).text = arguments?.getString(DIALOG_MESSAGE)
setView(view)
setPositiveButton("New Game") { _, _ ->
setPositiveButton(R.string.new_game) { _, _ ->
GlobalScope.launch {
viewModel.startNewGame()
}
}
setNeutralButton("Share") { _, _ ->}
setNeutralButton(R.string.share) { _, _ ->}
}.create()
}
companion object {
fun newInstance(message: String, victory: Boolean): EndGameDialogFragment =
EndGameDialogFragment().apply {
arguments = Bundle().apply {
putBoolean(DIALOG_STATE, victory)
putString(DIALOG_MESSAGE, message)}
}
private const val DIALOG_STATE = "dialog_state"
private const val DIALOG_MESSAGE = "dialog_message"
}
}

View file

@ -5,13 +5,12 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:padding="16dp">
<TextView
android:id="@+id/title_emoji"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="#FF000000"
android:textSize="52sp"
app:layout_constraintEnd_toEndOf="parent"
@ -24,13 +23,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/game_over"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_emoji" />
app:layout_constraintTop_toBottomOf="@+id/title_emoji"
tools:text="@string/you_lost" />
<TextView
android:id="@+id/subtitle"
@ -40,6 +40,7 @@
android:text="@string/you_lost"
android:textColor="@color/black"
android:textSize="14sp"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title"

View file

@ -1,74 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/appCompatTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="😥"
android:textSize="52sp"
android:textColor="#FF000000"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/game_over"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="@color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appCompatTextView" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="@string/you_lost"
android:textColor="@color/black"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView2"
tools:text="You did 5/12 in 34 seconds." />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3">
<Button
android:id="@+id/share"
style="?android:attr/borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Share" />
<Button
android:id="@+id/new_game"
style="?android:attr/borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/accent"
android:text="@string/new_game" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,7 +1,12 @@
package dev.lucasnlm.antimine.common.level.data
enum class GameStatus {
PreGame,
Running,
Over
sealed class GameStatus {
object PreGame : GameStatus()
object Running : GameStatus()
class Over(val rightMines: Int = 0,
val totalMines: Int = 0,
val time: Long = 0
) : GameStatus()
}

View file

@ -30,6 +30,7 @@
<string name="disconnect">Desconectar</string>
<string name="disconnected">Desconectado</string>
<string name="new_game">Nuevo juego</string>
<string name="share">Compartir</string>
<string name="new_game_request">¿Quieres iniciar un nuevo juego?</string>
<string name="yes"></string>
<string name="general">General</string>
@ -51,6 +52,11 @@
<string name="sure_quit_desc">Perderá todos los movimientos en el juego actual.</string>
<string name="you_won">¡Ganaste!</string>
<string name="you_lost">¡Perdiste!</string>
<string name="game_over_desc_0">Mantenga presionada un área para marcarla.</string>
<string name="game_over_desc_1">Buena suerte en el próximo juego.</string>
<string name="game_over_desc_2">Hiciste %1$d/%2$d en %3$d segundos.</string>
<string name="game_over_desc_3">¡Fue cerca! Sabes como hacerlo.</string>
<string name="game_over_desc_4">Terminaste el campo en %1$d segundos.</string>
<string name="version_s">%s - %s</string>
<string name="sound_effects">Efectos de Sonido</string>
<string name="quit">Dejar</string>

View file

@ -30,6 +30,7 @@
<string name="disconnect">Desconectar</string>
<string name="disconnected">Desconectado</string>
<string name="new_game">Novo Jogo</string>
<string name="share">Compartilhar</string>
<string name="new_game_request">Deseja começar um novo jogo?</string>
<string name="yes">Sim</string>
<string name="general">Geral</string>
@ -51,6 +52,11 @@
<string name="sure_quit_desc">Você perderá todos os movimentos do jogo atual.</string>
<string name="you_won">Você venceu!</string>
<string name="you_lost">Você perdeu!</string>
<string name="game_over_desc_0">Pressione e segure uma área para marcá-la.</string>
<string name="game_over_desc_1">Boa sorte no próximo jogo.</string>
<string name="game_over_desc_2">Você fez %1$d/%2$d em %3$d segundos.</string>
<string name="game_over_desc_3">Foi por pouco! Você quase conseguiu.</string>
<string name="game_over_desc_4">Você terminou o campo em %1$d segundos.</string>
<string name="version_s">%s - %s</string>
<string name="sound_effects">Efeitos Sonoros</string>
<string name="quit">Sair</string>

View file

@ -30,6 +30,7 @@
<string name="disconnect">断开</string>
<string name="disconnected">已断开</string>
<string name="new_game">新游戏</string>
<string name="share">分享</string>
<string name="new_game_request">你要开始一个新游戏吗?</string>
<string name="yes"></string>
<string name="general">常规</string>
@ -51,6 +52,11 @@
<string name="sure_quit_desc">你将丢失当前游戏中的所有进度。</string>
<string name="you_won">你赢了!</string>
<string name="you_lost">你输了!</string>
<string name="game_over_desc_0">长按方块以标记</string>
<string name="game_over_desc_1">祝您下一场比赛好运。</string>
<string name="game_over_desc_2">您在%3$d秒内完成了%1$d/%2$d分</string>
<string name="game_over_desc_3">您几乎做到了!</string>
<string name="game_over_desc_4">您在%1$d秒内完成了</string>
<string name="version_s">%s - %s</string>
<string name="sound_effects">音效</string>
<string name="quit">退出</string>
@ -77,7 +83,7 @@
<string name="settings_large_areas_desc"/>
<string name="rating_menu">评价应用</string>
<string name="rating_message"></string>
<string name="rating_button"></string>
<string name="rating_button_no"></string>
<string name="rating_message"/>
<string name="rating_button"/>
<string name="rating_button_no"/>
</resources>

View file

@ -30,6 +30,7 @@
<string name="disconnect">Disconnect</string>
<string name="disconnected">Disconnected</string>
<string name="new_game">New Game</string>
<string name="share">Share</string>
<string name="new_game_request">Do you want to start a new game?</string>
<string name="yes">Yes</string>
<string name="general">General</string>
@ -51,7 +52,11 @@
<string name="sure_quit_desc">You\'ll lose all moves on current game.</string>
<string name="you_won">You won!</string>
<string name="you_lost">You lost!</string>
<string name="game_over">Game Over!</string>
<string name="game_over_desc_0">Long press a square to put a flag.</string>
<string name="game_over_desc_1">Good luck on your next game.</string>
<string name="game_over_desc_2">You did %1$d/%2$d in %3$d seconds.</string>
<string name="game_over_desc_3">You almost did it!</string>
<string name="game_over_desc_4">You finished the minefield in %1$d seconds.</string>
<string name="version_s">%s - %s</string>
<string name="sound_effects">Sound Effects</string>
<string name="quit">Quit</string>