commit
026957bca4
64 changed files with 990 additions and 577 deletions
|
@ -27,14 +27,14 @@ import com.google.android.play.core.install.model.AppUpdateType
|
|||
import com.google.android.play.core.install.model.UpdateAvailability
|
||||
import dagger.android.support.DaggerAppCompatActivity
|
||||
import dev.lucasnlm.antimine.about.AboutActivity
|
||||
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.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.common.level.viewmodel.GameViewModelFactory
|
||||
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.Event
|
||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
import dev.lucasnlm.antimine.core.utils.isDarkModeEnabled
|
||||
import dev.lucasnlm.antimine.instant.InstantAppManager
|
||||
|
@ -65,9 +65,10 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
private lateinit var viewModel: GameViewModel
|
||||
private lateinit var shareViewModel: ShareViewModel
|
||||
|
||||
private var gameStatus: GameStatus = GameStatus.PreGame
|
||||
private var status: Status = Status.PreGame
|
||||
private val usingLargeArea by lazy { preferencesRepository.useLargeAreas() }
|
||||
private var totalMines: Int = 0
|
||||
private var totalArea: Int = 0
|
||||
private var rightMines: Int = 0
|
||||
private var currentTime: Long = 0
|
||||
|
||||
|
@ -120,8 +121,9 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
|
||||
field.observe(this@GameActivity, Observer { area ->
|
||||
val mines = area.filter { it.hasMine }
|
||||
totalArea = area.count()
|
||||
totalMines = mines.count()
|
||||
rightMines = mines.map { if (it.mark == Mark.Flag) 1 else 0 }.sum()
|
||||
rightMines = mines.map { if (it.mark.isFlag()) 1 else 0 }.sum()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -131,7 +133,7 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
drawer.closeDrawer(GravityCompat.START)
|
||||
viewModel.resumeGame()
|
||||
}
|
||||
gameStatus == GameStatus.Running && instantAppManager.isEnabled() -> showQuitConfirmation {
|
||||
status == Status.Running && instantAppManager.isEnabled() -> showQuitConfirmation {
|
||||
super.onBackPressed()
|
||||
}
|
||||
else -> super.onBackPressed()
|
||||
|
@ -140,9 +142,9 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (gameStatus == GameStatus.Running) {
|
||||
if (status == Status.Running) {
|
||||
viewModel.resumeGame()
|
||||
analyticsManager.sentEvent(Event.Resume())
|
||||
analyticsManager.sentEvent(Analytics.Resume())
|
||||
}
|
||||
|
||||
restartIfNeed()
|
||||
|
@ -151,16 +153,16 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
if (gameStatus == GameStatus.Running) {
|
||||
if (status == Status.Running) {
|
||||
viewModel.pauseGame()
|
||||
}
|
||||
|
||||
analyticsManager.sentEvent(Event.Quit())
|
||||
analyticsManager.sentEvent(Analytics.Quit())
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean =
|
||||
when (gameStatus) {
|
||||
is GameStatus.Over, is GameStatus.Running -> {
|
||||
when (status) {
|
||||
is Status.Over, is Status.Running -> {
|
||||
menuInflater.inflate(R.menu.top_menu_over, menu)
|
||||
true
|
||||
}
|
||||
|
@ -170,8 +172,8 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.reset) {
|
||||
|
||||
val confirmResign = gameStatus == GameStatus.Running
|
||||
analyticsManager.sentEvent(Event.TapGameReset(confirmResign))
|
||||
val confirmResign = status == Status.Running
|
||||
analyticsManager.sentEvent(Analytics.TapGameReset(confirmResign))
|
||||
|
||||
if (confirmResign) {
|
||||
newGameConfirmation {
|
||||
|
@ -224,17 +226,17 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
}
|
||||
|
||||
override fun onDrawerOpened(drawerView: View) {
|
||||
if (gameStatus is GameStatus.Over) {
|
||||
if (status is Status.Over) {
|
||||
viewModel.pauseGame()
|
||||
}
|
||||
analyticsManager.sentEvent(Event.OpenDrawer())
|
||||
analyticsManager.sentEvent(Analytics.OpenDrawer())
|
||||
}
|
||||
|
||||
override fun onDrawerClosed(drawerView: View) {
|
||||
if (gameStatus is GameStatus.Over) {
|
||||
if (status is Status.Over) {
|
||||
viewModel.resumeGame()
|
||||
}
|
||||
analyticsManager.sentEvent(Event.CloseDrawer())
|
||||
analyticsManager.sentEvent(Analytics.CloseDrawer())
|
||||
}
|
||||
|
||||
override fun onDrawerStateChanged(newState: Int) {
|
||||
|
@ -247,10 +249,10 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
var handled = true
|
||||
|
||||
when (item.itemId) {
|
||||
R.id.standard -> changeDifficulty(DifficultyPreset.Standard)
|
||||
R.id.beginner -> changeDifficulty(DifficultyPreset.Beginner)
|
||||
R.id.intermediate -> changeDifficulty(DifficultyPreset.Intermediate)
|
||||
R.id.expert -> changeDifficulty(DifficultyPreset.Expert)
|
||||
R.id.standard -> changeDifficulty(Difficulty.Standard)
|
||||
R.id.beginner -> changeDifficulty(Difficulty.Beginner)
|
||||
R.id.intermediate -> changeDifficulty(Difficulty.Intermediate)
|
||||
R.id.expert -> changeDifficulty(Difficulty.Expert)
|
||||
R.id.custom -> showCustomLevelDialog()
|
||||
R.id.about -> showAbout()
|
||||
R.id.settings -> showSettings()
|
||||
|
@ -280,21 +282,21 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
val shouldRequestRating = preferencesRepository.getBoolean(PREFERENCE_REQUEST_RATING, true)
|
||||
|
||||
if (current >= 4 && shouldRequestRating) {
|
||||
analyticsManager.sentEvent(Event.ShowRatingRequest(current))
|
||||
analyticsManager.sentEvent(Analytics.ShowRatingRequest(current))
|
||||
showRequestRating()
|
||||
}
|
||||
|
||||
preferencesRepository.putInt(PREFERENCE_USE_COUNT, current + 1)
|
||||
}
|
||||
|
||||
private fun onChangeDifficulty(difficulty: DifficultyPreset) {
|
||||
private fun onChangeDifficulty(difficulty: Difficulty) {
|
||||
navigationView.menu.apply {
|
||||
arrayOf(
|
||||
DifficultyPreset.Standard to findItem(R.id.standard),
|
||||
DifficultyPreset.Beginner to findItem(R.id.beginner),
|
||||
DifficultyPreset.Intermediate to findItem(R.id.intermediate),
|
||||
DifficultyPreset.Expert to findItem(R.id.expert),
|
||||
DifficultyPreset.Custom to findItem(R.id.custom)
|
||||
Difficulty.Standard to findItem(R.id.standard),
|
||||
Difficulty.Beginner to findItem(R.id.beginner),
|
||||
Difficulty.Intermediate to findItem(R.id.intermediate),
|
||||
Difficulty.Expert to findItem(R.id.expert),
|
||||
Difficulty.Custom to findItem(R.id.custom)
|
||||
).map {
|
||||
it.second to (if (it.first == difficulty) R.drawable.checked else R.drawable.unchecked)
|
||||
}.forEach { (menuItem, icon) ->
|
||||
|
@ -365,31 +367,40 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun showAbout() {
|
||||
analyticsManager.sentEvent(Event.OpenAbout())
|
||||
analyticsManager.sentEvent(Analytics.OpenAbout())
|
||||
Intent(this, AboutActivity::class.java).apply {
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSettings() {
|
||||
analyticsManager.sentEvent(Event.OpenSettings())
|
||||
analyticsManager.sentEvent(Analytics.OpenSettings())
|
||||
Intent(this, PreferencesActivity::class.java).apply {
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showEndGameDialog(victory: Boolean) {
|
||||
if (gameStatus is GameStatus.Over && !isFinishing) {
|
||||
val currentGameStatus = status
|
||||
if (currentGameStatus is Status.Over && !isFinishing) {
|
||||
if (supportFragmentManager.findFragmentByTag(EndGameDialogFragment.TAG) == null) {
|
||||
val over = gameStatus as GameStatus.Over
|
||||
EndGameDialogFragment.newInstance(victory, over.rightMines, over.totalMines, over.time).apply {
|
||||
val score = currentGameStatus.score
|
||||
EndGameDialogFragment.newInstance(
|
||||
victory,
|
||||
score?.rightMines ?: 0,
|
||||
score?.totalMines ?: 0,
|
||||
currentGameStatus.time
|
||||
).apply {
|
||||
showAllowingStateLoss(supportFragmentManager, EndGameDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun waitAndShowEndGameDialog(victory: Boolean, await: Long = DateUtils.SECOND_IN_MILLIS) {
|
||||
private fun waitAndShowEndGameDialog(
|
||||
victory: Boolean,
|
||||
await: Long = DateUtils.SECOND_IN_MILLIS
|
||||
) {
|
||||
if (await > 0L) {
|
||||
postDelayed(Handler(), {
|
||||
showEndGameDialog(victory)
|
||||
|
@ -399,8 +410,8 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeDifficulty(newDifficulty: DifficultyPreset) {
|
||||
if (gameStatus == GameStatus.PreGame) {
|
||||
private fun changeDifficulty(newDifficulty: Difficulty) {
|
||||
if (status == Status.PreGame) {
|
||||
GlobalScope.launch {
|
||||
viewModel.startNewGame(newDifficulty)
|
||||
}
|
||||
|
@ -413,45 +424,65 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun onGameEvent(event: GameEvent) {
|
||||
private fun onGameEvent(event: Event) {
|
||||
when (event) {
|
||||
GameEvent.ResumeGame -> {
|
||||
Event.ResumeGame -> {
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
GameEvent.StartNewGame -> {
|
||||
gameStatus = GameStatus.PreGame
|
||||
Event.StartNewGame -> {
|
||||
status = Status.PreGame
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
GameEvent.Resume, GameEvent.Running -> {
|
||||
gameStatus = GameStatus.Running
|
||||
Event.Resume, Event.Running -> {
|
||||
status = Status.Running
|
||||
viewModel.runClock()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
GameEvent.Victory -> {
|
||||
gameStatus = GameStatus.Over(rightMines, totalMines, currentTime)
|
||||
Event.Victory -> {
|
||||
val score = Score(
|
||||
rightMines,
|
||||
totalMines,
|
||||
totalArea
|
||||
)
|
||||
status = Status.Over(currentTime, score)
|
||||
viewModel.stopClock()
|
||||
viewModel.revealAllEmptyAreas()
|
||||
viewModel.victory()
|
||||
invalidateOptionsMenu()
|
||||
waitAndShowEndGameDialog(true, 0L)
|
||||
}
|
||||
GameEvent.GameOver -> {
|
||||
gameStatus = GameStatus.Over(rightMines, totalMines, currentTime)
|
||||
Event.GameOver -> {
|
||||
val score = Score(
|
||||
rightMines,
|
||||
totalMines,
|
||||
totalArea
|
||||
)
|
||||
status = Status.Over(currentTime, score)
|
||||
invalidateOptionsMenu()
|
||||
viewModel.stopClock()
|
||||
viewModel.gameOver()
|
||||
|
||||
waitAndShowEndGameDialog(false)
|
||||
}
|
||||
GameEvent.ResumeVictory -> {
|
||||
gameStatus = GameStatus.Over(rightMines, totalMines, currentTime)
|
||||
Event.ResumeVictory -> {
|
||||
val score = Score(
|
||||
rightMines,
|
||||
totalMines,
|
||||
totalArea
|
||||
)
|
||||
status = Status.Over(currentTime, score)
|
||||
invalidateOptionsMenu()
|
||||
viewModel.stopClock()
|
||||
|
||||
waitAndShowEndGameDialog(true)
|
||||
}
|
||||
GameEvent.ResumeGameOver -> {
|
||||
gameStatus = GameStatus.Over(rightMines, totalMines, currentTime)
|
||||
Event.ResumeGameOver -> {
|
||||
val score = Score(
|
||||
rightMines,
|
||||
totalMines,
|
||||
totalArea
|
||||
)
|
||||
status = Status.Over(currentTime, score)
|
||||
invalidateOptionsMenu()
|
||||
viewModel.stopClock()
|
||||
|
||||
|
@ -497,7 +528,7 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
private fun shareCurrentGame() {
|
||||
val levelSetup = viewModel.levelSetup.value
|
||||
val field = viewModel.field.value
|
||||
val spentTime: Long? = if (gameStatus is GameStatus.Over) currentTime else null
|
||||
val spentTime: Long? = if (status is Status.Over) currentTime else null
|
||||
GlobalScope.launch {
|
||||
shareViewModel.share(levelSetup, field, spentTime)
|
||||
}
|
||||
|
@ -530,7 +561,7 @@ class GameActivity : DaggerAppCompatActivity() {
|
|||
)
|
||||
}
|
||||
|
||||
analyticsManager.sentEvent(Event.TapRatingRequest(from))
|
||||
analyticsManager.sentEvent(Analytics.TapRatingRequest(from))
|
||||
preferencesRepository.putBoolean(PREFERENCE_REQUEST_RATING, false)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.multidex.MultiDex
|
|||
import dagger.android.AndroidInjector
|
||||
import dagger.android.support.DaggerApplication
|
||||
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.Event
|
||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
import dev.lucasnlm.antimine.di.AppModule
|
||||
import dev.lucasnlm.antimine.di.DaggerAppComponent
|
||||
import javax.inject.Inject
|
||||
|
@ -29,6 +29,6 @@ class MainApplication : DaggerApplication() {
|
|||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
analyticsManager.setup(applicationContext, mapOf())
|
||||
analyticsManager.sentEvent(Event.Open())
|
||||
analyticsManager.sentEvent(Analytics.Open())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,9 +21,10 @@ import com.google.android.play.core.install.model.AppUpdateType
|
|||
import com.google.android.play.core.install.model.UpdateAvailability
|
||||
import dagger.android.support.DaggerAppCompatActivity
|
||||
import dev.lucasnlm.antimine.about.AboutActivity
|
||||
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.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.common.level.viewmodel.GameViewModelFactory
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
|
@ -45,7 +46,11 @@ class TvGameActivity : DaggerAppCompatActivity() {
|
|||
|
||||
private lateinit var viewModel: GameViewModel
|
||||
|
||||
private var gameStatus: GameStatus = GameStatus.PreGame
|
||||
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
|
||||
|
||||
|
@ -68,26 +73,37 @@ class TvGameActivity : DaggerAppCompatActivity() {
|
|||
eventObserver.observe(this@TvGameActivity, Observer {
|
||||
onGameEvent(it)
|
||||
})
|
||||
|
||||
elapsedTimeSeconds.observe(this@TvGameActivity, Observer {
|
||||
timer.apply {
|
||||
visibility = if (it == 0L) View.GONE else View.VISIBLE
|
||||
text = DateUtils.formatElapsedTime(it)
|
||||
}
|
||||
currentTime = it
|
||||
})
|
||||
|
||||
mineCount.observe(this@TvGameActivity, Observer {
|
||||
minesCount.apply {
|
||||
visibility = View.VISIBLE
|
||||
text = it.toString()
|
||||
}
|
||||
})
|
||||
|
||||
difficulty.observe(this@TvGameActivity, Observer {
|
||||
// onChangeDifficulty(it)
|
||||
})
|
||||
|
||||
field.observe(this@TvGameActivity, Observer { 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 (gameStatus == GameStatus.Running) {
|
||||
if (status == Status.Running) {
|
||||
viewModel.resumeGame()
|
||||
}
|
||||
}
|
||||
|
@ -95,14 +111,14 @@ class TvGameActivity : DaggerAppCompatActivity() {
|
|||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
if (gameStatus == GameStatus.Running) {
|
||||
if (status == Status.Running) {
|
||||
viewModel.pauseGame()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean =
|
||||
when (gameStatus) {
|
||||
is GameStatus.Over, is GameStatus.Running -> {
|
||||
when (status) {
|
||||
is Status.Over, is Status.Running -> {
|
||||
menuInflater.inflate(R.menu.top_menu_over, menu)
|
||||
true
|
||||
}
|
||||
|
@ -111,7 +127,7 @@ class TvGameActivity : DaggerAppCompatActivity() {
|
|||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.reset) {
|
||||
if (gameStatus == GameStatus.Running) {
|
||||
if (status == Status.Running) {
|
||||
newGameConfirmation {
|
||||
GlobalScope.launch {
|
||||
viewModel.startNewGame()
|
||||
|
@ -202,7 +218,7 @@ class TvGameActivity : DaggerAppCompatActivity() {
|
|||
private fun waitAndShowConfirmNewGame() {
|
||||
if (keepConfirmingNewGame) {
|
||||
HandlerCompat.postDelayed(Handler(), {
|
||||
if (gameStatus is GameStatus.Over && !isFinishing) {
|
||||
if (status is Status.Over && !isFinishing) {
|
||||
AlertDialog.Builder(this, R.style.MyDialog).apply {
|
||||
setTitle(R.string.new_game)
|
||||
setMessage(R.string.new_game_request)
|
||||
|
@ -222,7 +238,7 @@ class TvGameActivity : DaggerAppCompatActivity() {
|
|||
|
||||
private fun waitAndShowGameOverConfirmNewGame() {
|
||||
HandlerCompat.postDelayed(Handler(), {
|
||||
if (gameStatus is GameStatus.Over && !isFinishing) {
|
||||
if (status is Status.Over && !isFinishing) {
|
||||
AlertDialog.Builder(this, R.style.MyDialog).apply {
|
||||
setTitle(R.string.you_lost)
|
||||
setMessage(R.string.new_game_request)
|
||||
|
@ -237,8 +253,8 @@ class TvGameActivity : DaggerAppCompatActivity() {
|
|||
}, null, DateUtils.SECOND_IN_MILLIS)
|
||||
}
|
||||
|
||||
private fun changeDifficulty(newDifficulty: DifficultyPreset) {
|
||||
if (gameStatus == GameStatus.PreGame) {
|
||||
private fun changeDifficulty(newDifficulty: Difficulty) {
|
||||
if (status == Status.PreGame) {
|
||||
GlobalScope.launch {
|
||||
viewModel.startNewGame(newDifficulty)
|
||||
}
|
||||
|
@ -251,37 +267,52 @@ class TvGameActivity : DaggerAppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun onGameEvent(event: GameEvent) {
|
||||
private fun onGameEvent(event: Event) {
|
||||
when (event) {
|
||||
GameEvent.ResumeGame -> {
|
||||
Event.ResumeGame -> {
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
GameEvent.StartNewGame -> {
|
||||
gameStatus = GameStatus.PreGame
|
||||
Event.StartNewGame -> {
|
||||
status = Status.PreGame
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
GameEvent.Resume, GameEvent.Running -> {
|
||||
gameStatus = GameStatus.Running
|
||||
Event.Resume, Event.Running -> {
|
||||
status = Status.Running
|
||||
viewModel.runClock()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
GameEvent.Victory -> {
|
||||
gameStatus = GameStatus.Over()
|
||||
Event.Victory -> {
|
||||
val score = Score(
|
||||
rightMines,
|
||||
totalMines,
|
||||
totalArea
|
||||
)
|
||||
status = Status.Over(currentTime, score)
|
||||
viewModel.stopClock()
|
||||
viewModel.revealAllEmptyAreas()
|
||||
invalidateOptionsMenu()
|
||||
showVictory()
|
||||
}
|
||||
GameEvent.GameOver -> {
|
||||
gameStatus = GameStatus.Over()
|
||||
Event.GameOver -> {
|
||||
val score = Score(
|
||||
rightMines,
|
||||
totalMines,
|
||||
totalArea
|
||||
)
|
||||
status = Status.Over(currentTime, score)
|
||||
invalidateOptionsMenu()
|
||||
viewModel.stopClock()
|
||||
viewModel.gameOver()
|
||||
|
||||
waitAndShowGameOverConfirmNewGame()
|
||||
}
|
||||
GameEvent.ResumeVictory, GameEvent.ResumeGameOver -> {
|
||||
gameStatus = GameStatus.Over()
|
||||
Event.ResumeVictory, Event.ResumeGameOver -> {
|
||||
val score = Score(
|
||||
rightMines,
|
||||
totalMines,
|
||||
totalArea
|
||||
)
|
||||
status = Status.Over(currentTime, score)
|
||||
invalidateOptionsMenu()
|
||||
viewModel.stopClock()
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import androidx.lifecycle.ViewModelProviders
|
|||
|
||||
import dagger.android.support.DaggerAppCompatDialogFragment
|
||||
import dev.lucasnlm.antimine.R
|
||||
import dev.lucasnlm.antimine.common.level.data.DifficultyPreset
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
|
||||
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModelFactory
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
|
@ -71,10 +71,16 @@ class CustomLevelDialogFragment : DaggerAppCompatDialogFragment() {
|
|||
height = height.coerceAtMost(50)
|
||||
mines = mines.coerceAtLeast(1)
|
||||
|
||||
preferencesRepository.updateCustomGameMode(LevelSetup(width, height, mines))
|
||||
preferencesRepository.updateCustomGameMode(
|
||||
Minefield(
|
||||
width,
|
||||
height,
|
||||
mines
|
||||
)
|
||||
)
|
||||
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
viewModel.startNewGame(DifficultyPreset.Custom)
|
||||
viewModel.startNewGame(Difficulty.Custom)
|
||||
}
|
||||
}
|
||||
}.create()
|
||||
|
|
|
@ -51,7 +51,7 @@ class EndGameDialogFragment : DaggerAppCompatDialogFragment() {
|
|||
time = getLong(DIALOG_TIME)
|
||||
rightMines = getInt(DIALOG_RIGHT_MINES)
|
||||
totalMines = getInt(DIALOG_TOTAL_MINES)
|
||||
hasValidData = true
|
||||
hasValidData = (totalMines > 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import dev.lucasnlm.antimine.common.R
|
||||
import dev.lucasnlm.antimine.common.level.view.UnlockedHorizontalScrollView
|
||||
import dagger.android.support.DaggerFragment
|
||||
import dev.lucasnlm.antimine.common.level.data.DifficultyPreset
|
||||
import dev.lucasnlm.antimine.common.level.data.GameEvent
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import dev.lucasnlm.antimine.common.level.models.Event
|
||||
import dev.lucasnlm.antimine.common.level.view.AreaAdapter
|
||||
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
|
||||
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModelFactory
|
||||
|
@ -102,24 +102,24 @@ open class LevelFragment : DaggerFragment() {
|
|||
})
|
||||
eventObserver.observe(viewLifecycleOwner, Observer {
|
||||
when (it) {
|
||||
GameEvent.ResumeGameOver, GameEvent.GameOver,
|
||||
GameEvent.Victory, GameEvent.ResumeVictory -> areaAdapter.setClickEnabled(false)
|
||||
Event.ResumeGameOver, Event.GameOver,
|
||||
Event.Victory, Event.ResumeVictory -> areaAdapter.setClickEnabled(false)
|
||||
else -> areaAdapter.setClickEnabled(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleNewGameDeeplink(): DifficultyPreset? {
|
||||
var result: DifficultyPreset? = null
|
||||
private fun handleNewGameDeeplink(): Difficulty? {
|
||||
var result: Difficulty? = null
|
||||
|
||||
activity?.intent?.data?.let { uri ->
|
||||
if (uri.scheme == DEFAULT_SCHEME) {
|
||||
result = when (uri.schemeSpecificPart.removePrefix("//new-game/")) {
|
||||
"beginner" -> DifficultyPreset.Beginner
|
||||
"intermediate" -> DifficultyPreset.Intermediate
|
||||
"expert" -> DifficultyPreset.Expert
|
||||
"standard" -> DifficultyPreset.Standard
|
||||
"beginner" -> Difficulty.Beginner
|
||||
"intermediate" -> Difficulty.Intermediate
|
||||
"expert" -> Difficulty.Expert
|
||||
"standard" -> Difficulty.Standard
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,12 +12,12 @@ import android.graphics.Typeface
|
|||
import androidx.core.content.FileProvider
|
||||
import dev.lucasnlm.antimine.BuildConfig
|
||||
import dev.lucasnlm.antimine.R
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.data.Mark
|
||||
import dev.lucasnlm.antimine.common.level.model.AreaPalette
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||
import dev.lucasnlm.antimine.common.level.models.AreaPalette
|
||||
import dev.lucasnlm.antimine.common.level.repository.DrawableRepository
|
||||
import dev.lucasnlm.antimine.common.level.view.AreaPaintSettings
|
||||
import dev.lucasnlm.antimine.common.level.models.AreaPaintSettings
|
||||
import dev.lucasnlm.antimine.common.level.view.paintOnCanvas
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -29,11 +29,11 @@ class ShareBuilder(
|
|||
) {
|
||||
private val context: Context = context.applicationContext
|
||||
|
||||
suspend fun share(levelSetup: LevelSetup, field: List<Area>, spentTime: Long?): Boolean {
|
||||
suspend fun share(minefield: Minefield, field: List<Area>, spentTime: Long?): Boolean {
|
||||
val rightMines = field.count { it.hasMine && it.mark == Mark.Flag }
|
||||
val totalMines = field.count { it.hasMine }
|
||||
|
||||
val file = createImage(levelSetup, field)
|
||||
val file = createImage(minefield, field)
|
||||
|
||||
return if (file != null) {
|
||||
shareFile(context, file, rightMines, totalMines, spentTime)
|
||||
|
@ -42,7 +42,7 @@ class ShareBuilder(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun createImage(levelSetup: LevelSetup, field: List<Area>): File? = withContext(Dispatchers.IO) {
|
||||
private suspend fun createImage(minefield: Minefield, field: List<Area>): File? = withContext(Dispatchers.IO) {
|
||||
val size = 38f
|
||||
val padding = 1f
|
||||
val radius = 2f
|
||||
|
@ -63,8 +63,8 @@ class ShareBuilder(
|
|||
radius
|
||||
)
|
||||
|
||||
val imageWidth = (levelSetup.width * size + padding * 2).toInt()
|
||||
val imageHeight = (levelSetup.height * size + padding * 2).toInt()
|
||||
val imageWidth = (minefield.width * size + padding * 2).toInt()
|
||||
val imageHeight = (minefield.height * size + padding * 2).toInt()
|
||||
val bitmap = Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
|
||||
|
@ -73,9 +73,9 @@ class ShareBuilder(
|
|||
style = Paint.Style.FILL
|
||||
})
|
||||
|
||||
for (x in 0 until levelSetup.width) {
|
||||
for (y in 0 until levelSetup.height) {
|
||||
val area = field[x + y * levelSetup.width]
|
||||
for (x in 0 until minefield.width) {
|
||||
for (y in 0 until minefield.height) {
|
||||
val area = field[x + y * minefield.width]
|
||||
canvas.save()
|
||||
canvas.translate(x * size + padding, y * size + padding)
|
||||
area.paintOnCanvas(
|
||||
|
|
|
@ -5,17 +5,17 @@ import android.widget.Toast
|
|||
import androidx.lifecycle.AndroidViewModel
|
||||
import dev.lucasnlm.antimine.R
|
||||
import dev.lucasnlm.antimine.share.ShareBuilder
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
|
||||
class ShareViewModel(
|
||||
application: Application
|
||||
) : AndroidViewModel(application) {
|
||||
private val context = getApplication<Application>().applicationContext
|
||||
|
||||
suspend fun share(levelSetup: LevelSetup?, field: List<Area>?, spentTime: Long?) {
|
||||
val result = if (levelSetup != null && field != null && field.isNotEmpty()) {
|
||||
ShareBuilder(context).share(levelSetup, field, spentTime)
|
||||
suspend fun share(minefield: Minefield?, field: List<Area>?, spentTime: Long?) {
|
||||
val result = if (minefield != null && field != null && field.isNotEmpty()) {
|
||||
ShareBuilder(context).share(minefield, field, spentTime)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<item
|
||||
android:id="@+id/nav_difficulty"
|
||||
android:title="@string/levelSetup">
|
||||
android:title="@string/minefield">
|
||||
|
||||
<menu>
|
||||
<item
|
||||
|
|
|
@ -6,9 +6,9 @@ import android.graphics.Canvas
|
|||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.data.Mark
|
||||
import dev.lucasnlm.antimine.common.level.model.AreaPalette
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||
import dev.lucasnlm.antimine.common.level.models.AreaPalette
|
||||
import dev.lucasnlm.antimine.common.level.repository.DrawableRepository
|
||||
import dev.lucasnlm.antimine.common.level.view.AreaAdapter
|
||||
import dev.lucasnlm.antimine.common.level.view.paintOnCanvas
|
||||
|
@ -115,7 +115,13 @@ class AreaScreenshot {
|
|||
repeat(8) {
|
||||
val id = it + 1
|
||||
screenshotTest(
|
||||
Area(0, 0, 0, isCovered = false, minesAround = id),
|
||||
Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
minesAround = id
|
||||
),
|
||||
"mines_around_$id.png"
|
||||
)
|
||||
}
|
||||
|
@ -126,7 +132,13 @@ class AreaScreenshot {
|
|||
repeat(8) {
|
||||
val id = it + 1
|
||||
screenshotTest(
|
||||
Area(0, 0, 0, isCovered = false, minesAround = id),
|
||||
Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
minesAround = id
|
||||
),
|
||||
"mines_around_${id}_ambient.png",
|
||||
true
|
||||
)
|
||||
|
@ -135,85 +147,173 @@ class AreaScreenshot {
|
|||
|
||||
@Test
|
||||
fun testCoveredAreaWithFlag() {
|
||||
val area = Area(0, 0, 0, isCovered = true, mark = Mark.Flag)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = true,
|
||||
mark = Mark.Flag
|
||||
)
|
||||
screenshotTest(area, "covered_flag.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCoveredAreaWithFlagAmbient() {
|
||||
val area = Area(0, 0, 0, isCovered = true, mark = Mark.Flag)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = true,
|
||||
mark = Mark.Flag
|
||||
)
|
||||
screenshotTest(area, "covered_flag_ambient.png", true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCoveredAreaWithQuestion() {
|
||||
val area = Area(0, 0, 0, isCovered = true, mark = Mark.Question)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = true,
|
||||
mark = Mark.Question
|
||||
)
|
||||
screenshotTest(area, "covered_question.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCoveredAreaWithQuestionAmbient() {
|
||||
val area = Area(0, 0, 0, isCovered = true, mark = Mark.Question)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = true,
|
||||
mark = Mark.Question
|
||||
)
|
||||
screenshotTest(area, "covered_question_ambient.png", true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCoveredAreaHighlighted() {
|
||||
val area = Area(0, 0, 0, isCovered = true, highlighted = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = true,
|
||||
highlighted = true
|
||||
)
|
||||
screenshotTest(area, "covered_highlighted.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCoveredAreaHighlightedAmbient() {
|
||||
val area = Area(0, 0, 0, isCovered = true, highlighted = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = true,
|
||||
highlighted = true
|
||||
)
|
||||
screenshotTest(area, "covered_highlighted_ambient.png", true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCoveredAreaWithMine() {
|
||||
val area = Area(0, 0, 0, isCovered = true, hasMine = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = true,
|
||||
hasMine = true
|
||||
)
|
||||
screenshotTest(area, "covered_mine.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCoveredAreaWithMineAmbient() {
|
||||
val area = Area(0, 0, 0, isCovered = true, hasMine = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = true,
|
||||
hasMine = true
|
||||
)
|
||||
screenshotTest(area, "covered_mine_ambient.png", true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUncoveredAreaWithMine() {
|
||||
val area = Area(0, 0, 0, isCovered = false, hasMine = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
hasMine = true
|
||||
)
|
||||
screenshotTest(area, "uncovered_mine.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUncoveredAreaWithMineAmbient() {
|
||||
val area = Area(0, 0, 0, isCovered = false, hasMine = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
hasMine = true
|
||||
)
|
||||
screenshotTest(area, "uncovered_mine_ambient.png", true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUncoveredAreaHighlighted() {
|
||||
val area = Area(0, 0, 0, isCovered = false, hasMine = false, highlighted = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
hasMine = false,
|
||||
highlighted = true
|
||||
)
|
||||
screenshotTest(area, "uncovered_highlighted.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUncoveredAreaHighlightedAmbient() {
|
||||
val area = Area(0, 0, 0, isCovered = false, hasMine = false, highlighted = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
hasMine = false,
|
||||
highlighted = true
|
||||
)
|
||||
screenshotTest(area, "uncovered_highlighted_ambient.png", true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUncoveredAreaWithMineExploded() {
|
||||
val area = Area(0, 0, 0, isCovered = false, hasMine = true, mistake = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
hasMine = true,
|
||||
mistake = true
|
||||
)
|
||||
screenshotTest(area, "uncovered_mine_exploded.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUncoveredAreaWithMineExplodedAmbient() {
|
||||
val area = Area(0, 0, 0, isCovered = false, hasMine = true, mistake = true)
|
||||
val area = Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
hasMine = true,
|
||||
mistake = true
|
||||
)
|
||||
screenshotTest(area, "uncovered_mine_exploded_ambient.png", true)
|
||||
}
|
||||
|
||||
|
@ -222,7 +322,14 @@ class AreaScreenshot {
|
|||
repeat(8) {
|
||||
val id = it + 1
|
||||
screenshotTest(
|
||||
Area(0, 0, 0, isCovered = false, minesAround = id, highlighted = true),
|
||||
Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
minesAround = id,
|
||||
highlighted = true
|
||||
),
|
||||
"mines_around_highlighted_$id.png"
|
||||
)
|
||||
}
|
||||
|
@ -233,7 +340,14 @@ class AreaScreenshot {
|
|||
repeat(8) {
|
||||
val id = it + 1
|
||||
screenshotTest(
|
||||
Area(0, 0, 0, isCovered = false, minesAround = id, highlighted = true),
|
||||
Area(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
isCovered = false,
|
||||
minesAround = id,
|
||||
highlighted = true
|
||||
),
|
||||
"mines_around_highlighted_${id}_ambient.png",
|
||||
true
|
||||
)
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
package dev.lucasnlm.antimine.common.level
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.data.DifficultyPreset
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
|
||||
object GameModeFactory {
|
||||
fun fromDifficultyPreset(
|
||||
difficulty: DifficultyPreset,
|
||||
dimensionRepository: IDimensionRepository,
|
||||
preferencesRepository: IPreferencesRepository
|
||||
): LevelSetup =
|
||||
when (difficulty) {
|
||||
DifficultyPreset.Standard -> calculateStandardMode(dimensionRepository)
|
||||
DifficultyPreset.Beginner -> LevelSetup(9, 9, 10, difficulty)
|
||||
DifficultyPreset.Intermediate -> LevelSetup(16, 16, 40, difficulty)
|
||||
DifficultyPreset.Expert -> LevelSetup(24, 24, 99, difficulty)
|
||||
DifficultyPreset.Custom -> preferencesRepository.customGameMode()
|
||||
}
|
||||
|
||||
private fun calculateStandardMode(
|
||||
dimensionRepository: IDimensionRepository
|
||||
): LevelSetup {
|
||||
val fieldSize = dimensionRepository.areaSize()
|
||||
|
||||
val display = dimensionRepository.displaySize()
|
||||
val width = display.widthPixels
|
||||
val height = display.heightPixels
|
||||
|
||||
val finalWidth = ((width / fieldSize).toInt() - 1).coerceAtLeast(6)
|
||||
val finalHeight = ((height / fieldSize).toInt() - 3).coerceAtLeast(9)
|
||||
|
||||
return LevelSetup(
|
||||
finalWidth,
|
||||
finalHeight,
|
||||
(finalWidth * finalHeight * 0.2).toInt(),
|
||||
DifficultyPreset.Standard
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,20 +1,18 @@
|
|||
package dev.lucasnlm.antimine.common.level
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.data.GameStats
|
||||
import dev.lucasnlm.antimine.common.level.data.Mark
|
||||
import dev.lucasnlm.antimine.common.level.data.isNone
|
||||
import dev.lucasnlm.antimine.common.level.data.isNotNone
|
||||
import dev.lucasnlm.antimine.common.level.database.data.Save
|
||||
import dev.lucasnlm.antimine.common.level.database.data.SaveStatus
|
||||
import dev.lucasnlm.antimine.common.level.database.models.Save
|
||||
import dev.lucasnlm.antimine.common.level.database.models.SaveStatus
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.common.level.models.Score
|
||||
import java.util.Random
|
||||
import kotlin.math.floor
|
||||
|
||||
class LevelFacade {
|
||||
private val levelSetup: LevelSetup
|
||||
private val minefield: Minefield
|
||||
private val randomGenerator: Random
|
||||
private var saveId = 0
|
||||
private val startTime = System.currentTimeMillis()
|
||||
|
||||
var hasMines = false
|
||||
|
@ -28,17 +26,15 @@ class LevelFacade {
|
|||
|
||||
private var mines: Sequence<Area> = sequenceOf()
|
||||
|
||||
constructor(gameId: Int, levelSetup: LevelSetup, seed: Long = randomSeed()) {
|
||||
this.saveId = gameId
|
||||
this.levelSetup = levelSetup
|
||||
constructor(minefield: Minefield, seed: Long = randomSeed()) {
|
||||
this.minefield = minefield
|
||||
this.randomGenerator = Random().apply { setSeed(seed) }
|
||||
this.seed = seed
|
||||
createEmptyField()
|
||||
}
|
||||
|
||||
constructor(save: Save) {
|
||||
this.saveId = save.uid
|
||||
this.levelSetup = save.levelSetup
|
||||
this.minefield = save.minefield
|
||||
this.randomGenerator = Random().apply { setSeed(save.seed) }
|
||||
this.field = save.field.asSequence()
|
||||
this.mines = this.field.filter { it.hasMine }.asSequence()
|
||||
|
@ -46,8 +42,8 @@ class LevelFacade {
|
|||
}
|
||||
|
||||
private fun createEmptyField() {
|
||||
val width = levelSetup.width
|
||||
val height = levelSetup.height
|
||||
val width = minefield.width
|
||||
val height = minefield.height
|
||||
val fieldSize = width * height
|
||||
this.field = (0 until fieldSize).map { index ->
|
||||
val yPosition = floor((index / width).toDouble()).toInt()
|
||||
|
@ -102,7 +98,7 @@ class LevelFacade {
|
|||
field.filterNot { it.safeZone }
|
||||
.toSet()
|
||||
.shuffled(randomGenerator)
|
||||
.take(levelSetup.mines)
|
||||
.take(minefield.mines)
|
||||
.forEach { it.hasMine = true }
|
||||
mines = field.filter { it.hasMine }.asSequence()
|
||||
hasMines = mines.count() != 0
|
||||
|
@ -180,22 +176,22 @@ class LevelFacade {
|
|||
fun runFlagAssistant() {
|
||||
// Must not select Mark.PurposefulNone, only Mark.None. Otherwise, it will flag
|
||||
// a square that was previously unflagged by player.
|
||||
mines.filter { it.mark == Mark.None }.forEach { field ->
|
||||
mines.filter { it.mark.isPureNone() }.forEach { field ->
|
||||
val neighbors = field.findNeighbors()
|
||||
val neighborsCount = neighbors.count()
|
||||
val revealedNeighborsCount = neighbors.filter { neighbor ->
|
||||
!neighbor.isCovered || (neighbor.hasMine && neighbor.mark == Mark.Flag)
|
||||
!neighbor.isCovered || (neighbor.hasMine && neighbor.mark.isFlag())
|
||||
}.count()
|
||||
|
||||
field.mark = if (revealedNeighborsCount == neighborsCount) Mark.Flag else Mark.None
|
||||
}
|
||||
}
|
||||
|
||||
fun getStats() = GameStats(
|
||||
mines.filter { !it.mistake && it.mark == Mark.Flag }.count(),
|
||||
mines.count(),
|
||||
field.count()
|
||||
)
|
||||
fun getStats() = Score(
|
||||
mines.filter { !it.mistake && it.mark.isFlag() }.count(),
|
||||
mines.count(),
|
||||
field.count()
|
||||
)
|
||||
|
||||
fun showAllMines() {
|
||||
mines.filter { it.mark != Mark.Flag }.forEach { it.isCovered = false }
|
||||
|
@ -215,7 +211,7 @@ class LevelFacade {
|
|||
|
||||
fun hasAnyMineExploded(): Boolean = mines.firstOrNull { it.mistake } != null
|
||||
|
||||
fun hasFlaggedAllMines(): Boolean = rightFlags() == levelSetup.mines
|
||||
fun hasFlaggedAllMines(): Boolean = rightFlags() == minefield.mines
|
||||
|
||||
fun hasIsolatedAllMines() =
|
||||
mines.map {
|
||||
|
@ -227,13 +223,13 @@ class LevelFacade {
|
|||
neighborsCount == isolatedNeighborsCount
|
||||
}.filterNot { it }.count() == 0
|
||||
|
||||
private fun rightFlags() = mines.count { it.mark == Mark.Flag }
|
||||
private fun rightFlags() = mines.count { it.mark.isFlag() }
|
||||
|
||||
fun checkVictory(): Boolean =
|
||||
hasMines && hasIsolatedAllMines() && !hasAnyMineExploded()
|
||||
|
||||
fun remainingMines(): Int {
|
||||
val flagsCount = field.count { it.mark == Mark.Flag }
|
||||
val flagsCount = field.count { it.mark.isFlag() }
|
||||
val minesCount = mines.count()
|
||||
return (minesCount - flagsCount).coerceAtLeast(0)
|
||||
}
|
||||
|
@ -253,13 +249,13 @@ class LevelFacade {
|
|||
(it.posX == this.posX + x) && (it.posY == this.posY + y)
|
||||
}
|
||||
|
||||
fun getSaveState(): Save {
|
||||
fun getSaveState(duration: Long, difficulty: Difficulty): Save {
|
||||
val saveStatus: SaveStatus = when {
|
||||
checkVictory() -> SaveStatus.VICTORY
|
||||
hasAnyMineExploded() -> SaveStatus.DEFEAT
|
||||
else -> SaveStatus.ON_GOING
|
||||
}
|
||||
return Save(saveId, seed, startTime, 0L, levelSetup, saveStatus, field.toList())
|
||||
return Save(0, seed, startTime, duration, minefield, difficulty, saveStatus, field.toList())
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package dev.lucasnlm.antimine.common.level.data
|
||||
|
||||
sealed class GameStatus {
|
||||
object PreGame : GameStatus()
|
||||
|
||||
object Running : GameStatus()
|
||||
|
||||
class Over(
|
||||
val rightMines: Int = 0,
|
||||
val totalMines: Int = 0,
|
||||
val time: Long = 0
|
||||
) : GameStatus()
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package dev.lucasnlm.antimine.common.level.data
|
||||
|
||||
data class LevelSetup(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val mines: Int,
|
||||
val preset: DifficultyPreset = DifficultyPreset.Custom
|
||||
)
|
|
@ -1,16 +0,0 @@
|
|||
package dev.lucasnlm.antimine.common.level.data
|
||||
|
||||
enum class Mark {
|
||||
None,
|
||||
Flag,
|
||||
Question,
|
||||
PurposefulNone
|
||||
}
|
||||
|
||||
fun Mark.isFlag(): Boolean = this == Mark.Flag
|
||||
|
||||
fun Mark.isQuestion(): Boolean = this == Mark.Question
|
||||
|
||||
fun Mark.isNone(): Boolean = this == Mark.None || this == Mark.PurposefulNone
|
||||
|
||||
fun Mark.isNotNone(): Boolean = this.isNone().not()
|
|
@ -3,21 +3,23 @@ package dev.lucasnlm.antimine.common.level.database
|
|||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import dev.lucasnlm.antimine.common.level.database.converters.DifficultyConverter
|
||||
import dev.lucasnlm.antimine.common.level.database.converters.FieldConverter
|
||||
import dev.lucasnlm.antimine.common.level.database.converters.LevelSetupConverter
|
||||
import dev.lucasnlm.antimine.common.level.database.converters.MinefieldConverter
|
||||
import dev.lucasnlm.antimine.common.level.database.converters.SaveStatusConverter
|
||||
import dev.lucasnlm.antimine.common.level.database.dao.SaveDao
|
||||
import dev.lucasnlm.antimine.common.level.database.data.Save
|
||||
import dev.lucasnlm.antimine.common.level.database.models.Save
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
Save::class
|
||||
], version = 1, exportSchema = false
|
||||
], version = 2, exportSchema = false
|
||||
)
|
||||
@TypeConverters(
|
||||
FieldConverter::class,
|
||||
SaveStatusConverter::class,
|
||||
LevelSetupConverter::class
|
||||
MinefieldConverter::class,
|
||||
DifficultyConverter::class
|
||||
)
|
||||
abstract class AppDataBase : RoomDatabase() {
|
||||
abstract fun userDao(): SaveDao
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
|
||||
class DifficultyConverter {
|
||||
|
||||
@TypeConverter
|
||||
fun toDifficulty(difficulty: Int): Difficulty =
|
||||
when (difficulty) {
|
||||
0 -> Difficulty.Standard
|
||||
1 -> Difficulty.Beginner
|
||||
2 -> Difficulty.Intermediate
|
||||
3 -> Difficulty.Expert
|
||||
else -> throw IllegalArgumentException("Could not recognize Difficulty")
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toInteger(difficulty: Difficulty): Int = difficulty.ordinal
|
||||
}
|
|
@ -5,7 +5,7 @@ import com.squareup.moshi.JsonAdapter
|
|||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class FieldConverter {
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import dev.lucasnlm.antimine.common.level.data.DifficultyPreset
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
|
||||
class LevelSetupConverter {
|
||||
private val moshi: Moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
private val jsonAdapter: JsonAdapter<LevelSetup>
|
||||
|
||||
init {
|
||||
this.jsonAdapter = moshi.adapter(LevelSetup::class.java)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toLevelSetup(jsonInput: String): LevelSetup =
|
||||
jsonAdapter.fromJson(jsonInput) ?: LevelSetup(9, 9, 10, DifficultyPreset.Beginner)
|
||||
|
||||
@TypeConverter
|
||||
fun toJsonString(field: LevelSetup): String = jsonAdapter.toJson(field)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
|
||||
class MinefieldConverter {
|
||||
private val moshi: Moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
private val jsonAdapter: JsonAdapter<Minefield> = moshi.adapter(
|
||||
Minefield::class.java)
|
||||
|
||||
@TypeConverter
|
||||
fun toMinefield(jsonInput: String): Minefield =
|
||||
jsonAdapter.fromJson(jsonInput) ?: Minefield(
|
||||
9,
|
||||
9,
|
||||
9
|
||||
)
|
||||
|
||||
@TypeConverter
|
||||
fun toJsonString(field: Minefield): String = jsonAdapter.toJson(field)
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.converters
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import dev.lucasnlm.antimine.common.level.database.data.SaveStatus
|
||||
import dev.lucasnlm.antimine.common.level.database.models.SaveStatus
|
||||
|
||||
class SaveStatusConverter {
|
||||
|
||||
@TypeConverter
|
||||
fun toSaveStatus(status: Int): SaveStatus =
|
||||
when (status) {
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.room.Delete
|
|||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import dev.lucasnlm.antimine.common.level.database.data.Save
|
||||
import dev.lucasnlm.antimine.common.level.database.models.Save
|
||||
|
||||
@Dao
|
||||
interface SaveDao {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.data
|
||||
package dev.lucasnlm.antimine.common.level.database.models
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.common.level.database.converters.FieldConverter
|
||||
import dev.lucasnlm.antimine.common.level.database.converters.SaveStatusConverter
|
||||
|
||||
|
@ -23,8 +24,11 @@ data class Save(
|
|||
@ColumnInfo(name = "duration")
|
||||
val duration: Long,
|
||||
|
||||
@ColumnInfo(name = "width")
|
||||
val levelSetup: LevelSetup,
|
||||
@ColumnInfo(name = "minefield")
|
||||
val minefield: Minefield,
|
||||
|
||||
@ColumnInfo(name = "difficulty")
|
||||
val difficulty: Difficulty,
|
||||
|
||||
@TypeConverters(SaveStatusConverter::class)
|
||||
@ColumnInfo(name = "status")
|
|
@ -1,10 +1,12 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.data
|
||||
package dev.lucasnlm.antimine.common.level.database.models
|
||||
|
||||
import androidx.room.TypeConverters
|
||||
import dev.lucasnlm.antimine.common.level.database.converters.SaveStatusConverter
|
||||
|
||||
@TypeConverters(SaveStatusConverter::class)
|
||||
enum class SaveStatus constructor(val code: Int) {
|
||||
enum class SaveStatus(
|
||||
val code: Int
|
||||
) {
|
||||
ON_GOING(0),
|
||||
VICTORY(1),
|
||||
DEFEAT(2)
|
|
@ -6,12 +6,13 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.room.Room
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dev.lucasnlm.antimine.common.level.data.GameEvent
|
||||
import dev.lucasnlm.antimine.common.level.models.Event
|
||||
import dev.lucasnlm.antimine.common.level.database.AppDataBase
|
||||
import dev.lucasnlm.antimine.common.level.database.dao.SaveDao
|
||||
import dev.lucasnlm.antimine.common.level.repository.DimensionRepository
|
||||
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
|
||||
import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
|
||||
import dev.lucasnlm.antimine.common.level.repository.MinefieldRepository
|
||||
import dev.lucasnlm.antimine.common.level.repository.SavesRepository
|
||||
import dev.lucasnlm.antimine.common.level.utils.Clock
|
||||
import dev.lucasnlm.antimine.common.level.utils.HapticFeedbackInteractor
|
||||
|
@ -23,7 +24,7 @@ import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
|||
@Module
|
||||
class LevelModule {
|
||||
@Provides
|
||||
fun provideGameEventObserver(): MutableLiveData<GameEvent> = MutableLiveData()
|
||||
fun provideGameEventObserver(): MutableLiveData<Event> = MutableLiveData()
|
||||
|
||||
@Provides
|
||||
fun provideClock(): Clock = Clock()
|
||||
|
@ -31,16 +32,24 @@ class LevelModule {
|
|||
@Provides
|
||||
fun provideGameViewModelFactory(
|
||||
application: Application,
|
||||
gameEventObserver: MutableLiveData<GameEvent>,
|
||||
eventObserver: MutableLiveData<Event>,
|
||||
savesRepository: ISavesRepository,
|
||||
dimensionRepository: IDimensionRepository,
|
||||
preferencesRepository: IPreferencesRepository,
|
||||
hapticFeedbackInteractor: IHapticFeedbackInteractor,
|
||||
minefieldRepository: MinefieldRepository,
|
||||
analyticsManager: AnalyticsManager,
|
||||
clock: Clock
|
||||
) = GameViewModelFactory(
|
||||
application, gameEventObserver, savesRepository,
|
||||
dimensionRepository, preferencesRepository, hapticFeedbackInteractor, analyticsManager, clock
|
||||
application,
|
||||
eventObserver,
|
||||
savesRepository,
|
||||
dimensionRepository,
|
||||
preferencesRepository,
|
||||
hapticFeedbackInteractor,
|
||||
minefieldRepository,
|
||||
analyticsManager,
|
||||
clock
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
@ -52,7 +61,9 @@ class LevelModule {
|
|||
|
||||
@Provides
|
||||
fun provideDataBase(application: Application): AppDataBase =
|
||||
Room.databaseBuilder(application, AppDataBase::class.java, DATA_BASE_NAME).build()
|
||||
Room.databaseBuilder(application, AppDataBase::class.java, DATA_BASE_NAME)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
|
||||
@Provides
|
||||
fun provideSaveDao(appDataBase: AppDataBase): SaveDao = appDataBase.userDao()
|
||||
|
@ -60,6 +71,9 @@ class LevelModule {
|
|||
@Provides
|
||||
fun provideSavesRepository(saveDao: SaveDao): ISavesRepository = SavesRepository(saveDao)
|
||||
|
||||
@Provides
|
||||
fun provideMinefieldRepository(): MinefieldRepository = MinefieldRepository()
|
||||
|
||||
@Provides
|
||||
fun provideHapticFeedbackInteractor(
|
||||
application: Application,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package dev.lucasnlm.antimine.common.level.data
|
||||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
data class AmbientSettings(
|
||||
val isAmbientMode: Boolean = false,
|
|
@ -1,4 +1,4 @@
|
|||
package dev.lucasnlm.antimine.common.level.data
|
||||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
data class Area(
|
||||
val id: Int,
|
|
@ -1,4 +1,4 @@
|
|||
package dev.lucasnlm.antimine.common.level.view
|
||||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
|
@ -1,4 +1,4 @@
|
|||
package dev.lucasnlm.antimine.common.level.model
|
||||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
|
@ -1,6 +1,8 @@
|
|||
package dev.lucasnlm.antimine.common.level.data
|
||||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
enum class DifficultyPreset(val text: String) {
|
||||
enum class Difficulty(
|
||||
val text: String
|
||||
) {
|
||||
Standard("STANDARD"),
|
||||
Beginner("BEGINNER"),
|
||||
Intermediate("INTERMEDIATE"),
|
|
@ -1,6 +1,6 @@
|
|||
package dev.lucasnlm.antimine.common.level.data
|
||||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
enum class GameEvent {
|
||||
enum class Event {
|
||||
StartNewGame,
|
||||
ResumeGame,
|
||||
ResumeGameOver,
|
|
@ -0,0 +1,18 @@
|
|||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
enum class Mark {
|
||||
None,
|
||||
Flag,
|
||||
Question,
|
||||
PurposefulNone;
|
||||
|
||||
fun isFlag(): Boolean = this == Flag
|
||||
|
||||
fun isQuestion(): Boolean = this == Question
|
||||
|
||||
fun isPureNone(): Boolean = this == None
|
||||
|
||||
fun isNone(): Boolean = this == None || this == PurposefulNone
|
||||
|
||||
fun isNotNone(): Boolean = this.isNone().not()
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
data class Minefield(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val mines: Int
|
||||
)
|
|
@ -1,6 +1,6 @@
|
|||
package dev.lucasnlm.antimine.common.level.data
|
||||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
data class GameStats(
|
||||
data class Score(
|
||||
val rightMines: Int,
|
||||
val totalMines: Int,
|
||||
val totalArea: Int
|
|
@ -0,0 +1,12 @@
|
|||
package dev.lucasnlm.antimine.common.level.models
|
||||
|
||||
sealed class Status {
|
||||
object PreGame : Status()
|
||||
|
||||
object Running : Status()
|
||||
|
||||
class Over(
|
||||
val time: Long = 0L,
|
||||
val score: Score? = null
|
||||
) : Status()
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package dev.lucasnlm.antimine.common.level.repository
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
|
||||
interface IMinefieldRepository {
|
||||
fun fromDifficulty(
|
||||
difficulty: Difficulty,
|
||||
dimensionRepository: IDimensionRepository,
|
||||
preferencesRepository: IPreferencesRepository
|
||||
): Minefield
|
||||
}
|
||||
|
||||
class MinefieldRepository : IMinefieldRepository {
|
||||
override fun fromDifficulty(
|
||||
difficulty: Difficulty,
|
||||
dimensionRepository: IDimensionRepository,
|
||||
preferencesRepository: IPreferencesRepository
|
||||
): Minefield =
|
||||
when (difficulty) {
|
||||
Difficulty.Standard -> calculateStandardMode(
|
||||
dimensionRepository
|
||||
)
|
||||
Difficulty.Beginner -> Minefield(
|
||||
9,
|
||||
9,
|
||||
10
|
||||
)
|
||||
Difficulty.Intermediate -> Minefield(
|
||||
16,
|
||||
16,
|
||||
40
|
||||
)
|
||||
Difficulty.Expert -> Minefield(
|
||||
24,
|
||||
24,
|
||||
99
|
||||
)
|
||||
Difficulty.Custom -> preferencesRepository.customGameMode()
|
||||
}
|
||||
|
||||
private fun calculateStandardMode(
|
||||
dimensionRepository: IDimensionRepository
|
||||
): Minefield {
|
||||
val fieldSize = dimensionRepository.areaSize()
|
||||
|
||||
val display = dimensionRepository.displaySize()
|
||||
val width = display.widthPixels
|
||||
val height = display.heightPixels
|
||||
|
||||
val finalWidth = ((width / fieldSize).toInt() - 1).coerceAtLeast(6)
|
||||
val finalHeight = ((height / fieldSize).toInt() - 3).coerceAtLeast(9)
|
||||
|
||||
return Minefield(
|
||||
finalWidth,
|
||||
finalHeight,
|
||||
(finalWidth * finalHeight * 0.2).toInt()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
package dev.lucasnlm.antimine.common.level.repository
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.database.dao.SaveDao
|
||||
import dev.lucasnlm.antimine.common.level.database.data.Save
|
||||
import dev.lucasnlm.antimine.common.level.database.models.Save
|
||||
import javax.inject.Inject
|
||||
|
||||
interface ISavesRepository {
|
||||
suspend fun getNewSaveId(): Int
|
||||
suspend fun fetchCurrentSave(): Save?
|
||||
suspend fun saveGame(save: Save): Long?
|
||||
}
|
||||
|
@ -14,8 +13,6 @@ class SavesRepository @Inject constructor(
|
|||
private val savesDao: SaveDao
|
||||
) : ISavesRepository {
|
||||
|
||||
override suspend fun getNewSaveId(): Int = savesDao.getSaveCounts() + 1
|
||||
|
||||
override suspend fun fetchCurrentSave(): Save? = savesDao.loadCurrent()
|
||||
|
||||
override suspend fun saveGame(save: Save) = savesDao.insertAll(save).firstOrNull()
|
||||
|
|
|
@ -5,17 +5,19 @@ import android.graphics.Paint
|
|||
import android.graphics.RectF
|
||||
import android.graphics.Typeface
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dev.lucasnlm.antimine.common.R
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.AreaPaintSettings
|
||||
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
|
||||
|
||||
class AreaAdapter(
|
||||
context: Context,
|
||||
private val viewModel: GameViewModel
|
||||
) : RecyclerView.Adapter<FieldViewHolder>() {
|
||||
) : RecyclerView.Adapter<AreaViewHolder>() {
|
||||
|
||||
private var field = listOf<Area>()
|
||||
private var isLowBitAmbient = false
|
||||
|
@ -23,6 +25,7 @@ class AreaAdapter(
|
|||
private val paintSettings: AreaPaintSettings
|
||||
|
||||
private var clickEnabled: Boolean = false
|
||||
private var longPressAt: Long = 0L
|
||||
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
|
@ -45,14 +48,14 @@ class AreaAdapter(
|
|||
|
||||
override fun getItemCount() = field.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FieldViewHolder {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AreaViewHolder {
|
||||
val layout = if (viewModel.useAccessibilityMode()) {
|
||||
R.layout.view_accessibility_field
|
||||
} else {
|
||||
R.layout.view_field
|
||||
}
|
||||
val view = LayoutInflater.from(parent.context).inflate(layout, parent, false)
|
||||
val holder = FieldViewHolder(view)
|
||||
val holder = AreaViewHolder(view)
|
||||
|
||||
holder.itemView.setOnLongClickListener { target ->
|
||||
target.requestFocus()
|
||||
|
@ -76,6 +79,33 @@ class AreaAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
holder.areaView.setOnKeyListener { _, keyCode, keyEvent ->
|
||||
var handled = false
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
when (keyEvent.action) {
|
||||
KeyEvent.ACTION_DOWN -> {
|
||||
longPressAt = System.currentTimeMillis()
|
||||
handled = true
|
||||
}
|
||||
KeyEvent.ACTION_UP -> {
|
||||
if (clickEnabled) {
|
||||
val value = System.currentTimeMillis() - longPressAt
|
||||
if (value > 300L) {
|
||||
view.performLongClick()
|
||||
} else {
|
||||
view.callOnClick()
|
||||
}
|
||||
}
|
||||
longPressAt = System.currentTimeMillis()
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handled
|
||||
}
|
||||
|
||||
return holder
|
||||
}
|
||||
|
||||
|
@ -83,7 +113,7 @@ class AreaAdapter(
|
|||
|
||||
override fun getItemId(position: Int): Long = getItem(position).id.toLong()
|
||||
|
||||
override fun onBindViewHolder(holder: FieldViewHolder, position: Int) {
|
||||
override fun onBindViewHolder(holder: AreaViewHolder, position: Int) {
|
||||
val field = getItem(position)
|
||||
holder.areaView.bindField(field, isAmbientMode, isLowBitAmbient, paintSettings)
|
||||
}
|
||||
|
@ -93,7 +123,6 @@ class AreaAdapter(
|
|||
|
||||
fun createAreaPaintSettings(context: Context, useLargeArea: Boolean): AreaPaintSettings {
|
||||
val resources = context.resources
|
||||
val padding = resources.getDimension(R.dimen.field_padding)
|
||||
val size = if (useLargeArea) {
|
||||
resources.getDimension(R.dimen.accessible_field_size)
|
||||
} else {
|
||||
|
|
|
@ -5,9 +5,10 @@ import android.graphics.Canvas
|
|||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import dev.lucasnlm.antimine.common.R
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.data.Mark
|
||||
import dev.lucasnlm.antimine.common.level.model.AreaPalette
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.AreaPaintSettings
|
||||
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||
import dev.lucasnlm.antimine.common.level.models.AreaPalette
|
||||
import dev.lucasnlm.antimine.common.level.repository.DrawableRepository
|
||||
|
||||
fun Area.paintOnCanvas(
|
||||
|
|
|
@ -6,16 +6,16 @@ import android.graphics.Canvas
|
|||
import androidx.core.content.ContextCompat
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.repository.DrawableRepository
|
||||
import android.util.TypedValue
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import androidx.core.view.ViewCompat
|
||||
import dev.lucasnlm.antimine.common.R
|
||||
import dev.lucasnlm.antimine.common.level.data.Mark
|
||||
import dev.lucasnlm.antimine.common.level.data.isNotNone
|
||||
import dev.lucasnlm.antimine.common.level.model.AreaPalette
|
||||
import dev.lucasnlm.antimine.common.level.models.AreaPaintSettings
|
||||
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||
import dev.lucasnlm.antimine.common.level.models.AreaPalette
|
||||
|
||||
class AreaView : View {
|
||||
// Used on Wear OS
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package dev.lucasnlm.antimine.common.level.view
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dev.lucasnlm.antimine.common.R
|
||||
|
||||
class AreaViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val areaView: AreaView = view.findViewById(R.id.area)
|
||||
|
||||
init {
|
||||
view.isFocusable = false
|
||||
areaView.isFocusable = true
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package dev.lucasnlm.antimine.common.level.view
|
||||
|
||||
import android.os.Build
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dev.lucasnlm.antimine.common.R
|
||||
|
||||
class FieldViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val areaView: AreaView = view.findViewById(R.id.area)
|
||||
|
||||
init {
|
||||
view.isFocusable = false
|
||||
areaView.isFocusable = true
|
||||
areaView.setOnKeyListener { _, keyCode, keyEvent ->
|
||||
var handled = false
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
|
||||
when (keyEvent.action) {
|
||||
KeyEvent.ACTION_DOWN -> {
|
||||
longPressAt = System.currentTimeMillis()
|
||||
handled = true
|
||||
}
|
||||
KeyEvent.ACTION_UP -> {
|
||||
if (System.currentTimeMillis() - longPressAt > 300L) {
|
||||
view.performLongClick()
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
|
||||
view.callOnClick()
|
||||
} else {
|
||||
view.performClick()
|
||||
}
|
||||
}
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handled
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
var longPressAt: Long = 0L
|
||||
}
|
||||
}
|
|
@ -3,19 +3,19 @@ package dev.lucasnlm.antimine.common.level.viewmodel
|
|||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dev.lucasnlm.antimine.common.level.GameModeFactory
|
||||
import dev.lucasnlm.antimine.common.level.repository.MinefieldRepository
|
||||
import dev.lucasnlm.antimine.common.level.LevelFacade
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.data.DifficultyPreset
|
||||
import dev.lucasnlm.antimine.common.level.data.GameEvent
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.database.data.Save
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import dev.lucasnlm.antimine.common.level.models.Event
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.common.level.database.models.Save
|
||||
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
|
||||
import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
|
||||
import dev.lucasnlm.antimine.common.level.utils.Clock
|
||||
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
|
||||
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
|
||||
import dev.lucasnlm.antimine.core.analytics.Event
|
||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
@ -24,89 +24,88 @@ import kotlinx.coroutines.withContext
|
|||
|
||||
class GameViewModel(
|
||||
val application: Application,
|
||||
val eventObserver: MutableLiveData<GameEvent>,
|
||||
val eventObserver: MutableLiveData<Event>,
|
||||
private val savesRepository: ISavesRepository,
|
||||
private val dimensionRepository: IDimensionRepository,
|
||||
private val preferencesRepository: IPreferencesRepository,
|
||||
private val hapticFeedbackInteractor: IHapticFeedbackInteractor,
|
||||
private val minefieldRepository: MinefieldRepository,
|
||||
private val analyticsManager: AnalyticsManager,
|
||||
private val clock: Clock
|
||||
) : ViewModel() {
|
||||
private lateinit var levelFacade: LevelFacade
|
||||
private var currentDifficulty: DifficultyPreset = DifficultyPreset.Standard
|
||||
private var currentDifficulty: Difficulty = Difficulty.Standard
|
||||
private var initialized = false
|
||||
|
||||
val field = MutableLiveData<List<Area>>()
|
||||
val fieldRefresh = MutableLiveData<Int>()
|
||||
val elapsedTimeSeconds = MutableLiveData<Long>()
|
||||
val mineCount = MutableLiveData<Int>()
|
||||
val difficulty = MutableLiveData<DifficultyPreset>()
|
||||
val levelSetup = MutableLiveData<LevelSetup>()
|
||||
val difficulty = MutableLiveData<Difficulty>()
|
||||
val levelSetup = MutableLiveData<Minefield>()
|
||||
|
||||
private fun startNewGame(gameId: Int, difficultyPreset: DifficultyPreset): LevelSetup {
|
||||
fun startNewGame(difficulty: Difficulty = currentDifficulty): Minefield {
|
||||
clock.reset()
|
||||
elapsedTimeSeconds.postValue(0L)
|
||||
currentDifficulty = difficultyPreset
|
||||
currentDifficulty = difficulty
|
||||
|
||||
val setup = GameModeFactory.fromDifficultyPreset(
|
||||
difficultyPreset, dimensionRepository, preferencesRepository
|
||||
val minefield = minefieldRepository.fromDifficulty(
|
||||
difficulty, dimensionRepository, preferencesRepository
|
||||
)
|
||||
|
||||
levelFacade = LevelFacade(gameId, setup)
|
||||
levelFacade = LevelFacade(minefield)
|
||||
|
||||
mineCount.postValue(setup.mines)
|
||||
difficulty.postValue(difficultyPreset)
|
||||
levelSetup.postValue(setup)
|
||||
mineCount.postValue(minefield.mines)
|
||||
this.difficulty.postValue(difficulty)
|
||||
levelSetup.postValue(minefield)
|
||||
field.postValue(levelFacade.field.toList())
|
||||
|
||||
eventObserver.postValue(GameEvent.StartNewGame)
|
||||
eventObserver.postValue(Event.StartNewGame)
|
||||
|
||||
analyticsManager.sentEvent(Event.NewGame(setup, levelFacade.seed, useAccessibilityMode()))
|
||||
analyticsManager.sentEvent(
|
||||
Analytics.NewGame(
|
||||
minefield, difficulty,
|
||||
levelFacade.seed,
|
||||
useAccessibilityMode()
|
||||
)
|
||||
)
|
||||
|
||||
return setup
|
||||
return minefield
|
||||
}
|
||||
|
||||
private fun resumeGameFromSave(save: Save): LevelSetup {
|
||||
private fun resumeGameFromSave(save: Save): Minefield {
|
||||
clock.reset(save.duration)
|
||||
elapsedTimeSeconds.postValue(save.duration)
|
||||
|
||||
val setup = save.levelSetup
|
||||
val setup = save.minefield
|
||||
levelFacade = LevelFacade(save)
|
||||
|
||||
mineCount.postValue(setup.mines)
|
||||
difficulty.postValue(save.levelSetup.preset)
|
||||
difficulty.postValue(save.difficulty)
|
||||
levelSetup.postValue(setup)
|
||||
field.postValue(levelFacade.field.toList())
|
||||
|
||||
when {
|
||||
levelFacade.hasAnyMineExploded() -> eventObserver.postValue(GameEvent.ResumeGameOver)
|
||||
levelFacade.checkVictory() -> eventObserver.postValue(GameEvent.ResumeVictory)
|
||||
else -> eventObserver.postValue(GameEvent.ResumeGame)
|
||||
levelFacade.hasAnyMineExploded() -> eventObserver.postValue(Event.ResumeGameOver)
|
||||
levelFacade.checkVictory() -> eventObserver.postValue(Event.ResumeVictory)
|
||||
else -> eventObserver.postValue(Event.ResumeGame)
|
||||
}
|
||||
|
||||
analyticsManager.sentEvent(Event.ResumePreviousGame())
|
||||
|
||||
analyticsManager.sentEvent(Analytics.ResumePreviousGame())
|
||||
return setup
|
||||
}
|
||||
|
||||
suspend fun startNewGame(difficultyPreset: DifficultyPreset = currentDifficulty): LevelSetup =
|
||||
withContext(Dispatchers.IO) {
|
||||
val newGameId = savesRepository.getNewSaveId()
|
||||
startNewGame(newGameId, difficultyPreset)
|
||||
}
|
||||
|
||||
suspend fun onCreate(newGame: DifficultyPreset? = null): LevelSetup = withContext(Dispatchers.IO) {
|
||||
suspend fun onCreate(newGame: Difficulty? = null): Minefield = withContext(Dispatchers.IO) {
|
||||
val lastGame = if (newGame == null) savesRepository.fetchCurrentSave() else null
|
||||
|
||||
if (lastGame != null) {
|
||||
currentDifficulty = lastGame.levelSetup.preset
|
||||
currentDifficulty = lastGame.difficulty
|
||||
} else if (newGame != null) {
|
||||
currentDifficulty = newGame
|
||||
}
|
||||
|
||||
if (lastGame == null) {
|
||||
val newGameId = savesRepository.getNewSaveId()
|
||||
startNewGame(newGameId, currentDifficulty)
|
||||
startNewGame(currentDifficulty)
|
||||
} else {
|
||||
resumeGameFromSave(lastGame)
|
||||
}.also {
|
||||
|
@ -117,26 +116,24 @@ class GameViewModel(
|
|||
fun pauseGame() {
|
||||
if (initialized) {
|
||||
if (levelFacade.hasMines) {
|
||||
eventObserver.postValue(GameEvent.Pause)
|
||||
eventObserver.postValue(Event.Pause)
|
||||
}
|
||||
clock.stop()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun saveGame() {
|
||||
if (initialized) {
|
||||
if (levelFacade.hasMines) {
|
||||
savesRepository.saveGame(
|
||||
levelFacade.getSaveState().copy(duration = elapsedTimeSeconds.value ?: 0L)
|
||||
)
|
||||
}
|
||||
if (initialized && levelFacade.hasMines) {
|
||||
savesRepository.saveGame(
|
||||
levelFacade.getSaveState(elapsedTimeSeconds.value ?: 0L, currentDifficulty)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun resumeGame() {
|
||||
if (initialized) {
|
||||
if (levelFacade.hasMines) {
|
||||
eventObserver.postValue(GameEvent.Resume)
|
||||
eventObserver.postValue(Event.Resume)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,11 +147,11 @@ class GameViewModel(
|
|||
hapticFeedbackInteractor.toggleFlagFeedback()
|
||||
}
|
||||
|
||||
analyticsManager.sentEvent(Event.LongPressArea(index))
|
||||
analyticsManager.sentEvent(Analytics.LongPressArea(index))
|
||||
} else {
|
||||
levelFacade.openNeighbors(index)
|
||||
|
||||
analyticsManager.sentEvent(Event.LongPressMultipleArea(index))
|
||||
analyticsManager.sentEvent(Analytics.LongPressMultipleArea(index))
|
||||
}
|
||||
|
||||
field.postValue(levelFacade.field.toList())
|
||||
|
@ -184,7 +181,7 @@ class GameViewModel(
|
|||
}
|
||||
|
||||
refreshGame()
|
||||
analyticsManager.sentEvent(Event.PressArea(index))
|
||||
analyticsManager.sentEvent(Analytics.PressArea(index))
|
||||
}
|
||||
|
||||
private fun refreshMineCount() = mineCount.postValue(levelFacade.remainingMines())
|
||||
|
@ -193,10 +190,10 @@ class GameViewModel(
|
|||
when {
|
||||
levelFacade.hasAnyMineExploded() -> {
|
||||
hapticFeedbackInteractor.explosionFeedback()
|
||||
eventObserver.postValue(GameEvent.GameOver)
|
||||
eventObserver.postValue(Event.GameOver)
|
||||
}
|
||||
else -> {
|
||||
eventObserver.postValue(GameEvent.Running)
|
||||
eventObserver.postValue(Event.Running)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,7 +202,7 @@ class GameViewModel(
|
|||
}
|
||||
|
||||
if (levelFacade.checkVictory()) {
|
||||
eventObserver.postValue(GameEvent.Victory)
|
||||
eventObserver.postValue(Event.Victory)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,7 +224,7 @@ class GameViewModel(
|
|||
|
||||
fun gameOver() {
|
||||
levelFacade.run {
|
||||
analyticsManager.sentEvent(Event.GameOver(clock.time(), getStats()))
|
||||
analyticsManager.sentEvent(Analytics.GameOver(clock.time(), getStats()))
|
||||
showAllMines()
|
||||
showWrongFlags()
|
||||
}
|
||||
|
@ -239,7 +236,13 @@ class GameViewModel(
|
|||
|
||||
fun victory() {
|
||||
levelFacade.run {
|
||||
analyticsManager.sentEvent(Event.Victory(clock.time(), getStats(), currentDifficulty))
|
||||
analyticsManager.sentEvent(
|
||||
Analytics.Victory(
|
||||
clock.time(),
|
||||
getStats(),
|
||||
currentDifficulty
|
||||
)
|
||||
)
|
||||
flagAllMines()
|
||||
showWrongFlags()
|
||||
}
|
||||
|
|
|
@ -4,9 +4,10 @@ import android.app.Application
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import dev.lucasnlm.antimine.common.level.data.GameEvent
|
||||
import dev.lucasnlm.antimine.common.level.models.Event
|
||||
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
|
||||
import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
|
||||
import dev.lucasnlm.antimine.common.level.repository.MinefieldRepository
|
||||
import dev.lucasnlm.antimine.common.level.utils.Clock
|
||||
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
|
||||
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
|
||||
|
@ -15,11 +16,12 @@ import javax.inject.Inject
|
|||
|
||||
class GameViewModelFactory @Inject constructor(
|
||||
private val application: Application,
|
||||
private val gameEventObserver: MutableLiveData<GameEvent>,
|
||||
private val eventObserver: MutableLiveData<Event>,
|
||||
private val savesRepository: ISavesRepository,
|
||||
private val dimensionRepository: IDimensionRepository,
|
||||
private val preferencesRepository: IPreferencesRepository,
|
||||
private val hapticFeedbackInteractor: IHapticFeedbackInteractor,
|
||||
private val minefieldRepository: MinefieldRepository,
|
||||
private val analyticsManager: AnalyticsManager,
|
||||
private val clock: Clock
|
||||
) : ViewModelProvider.Factory {
|
||||
|
@ -28,9 +30,15 @@ class GameViewModelFactory @Inject constructor(
|
|||
if (modelClass.isAssignableFrom(GameViewModel::class.java)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
GameViewModel(
|
||||
application, gameEventObserver, savesRepository,
|
||||
dimensionRepository, preferencesRepository, hapticFeedbackInteractor,
|
||||
analyticsManager, clock
|
||||
application,
|
||||
eventObserver,
|
||||
savesRepository,
|
||||
dimensionRepository,
|
||||
preferencesRepository,
|
||||
hapticFeedbackInteractor,
|
||||
minefieldRepository,
|
||||
analyticsManager,
|
||||
clock
|
||||
) as T
|
||||
} else {
|
||||
throw IllegalArgumentException("ViewModel Not Found")
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||
import com.amplitude.api.Amplitude
|
||||
import com.amplitude.api.AmplitudeClient
|
||||
import dev.lucasnlm.antimine.common.R
|
||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
import org.json.JSONObject
|
||||
|
||||
class AmplitudeAnalyticsManager(
|
||||
|
@ -21,7 +22,7 @@ class AmplitudeAnalyticsManager(
|
|||
}
|
||||
}
|
||||
|
||||
override fun sentEvent(event: Event) {
|
||||
override fun sentEvent(event: Analytics) {
|
||||
amplitudeClient?.logEvent(event.title, JSONObject(event.extra))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package dev.lucasnlm.antimine.core.analytics
|
||||
|
||||
import android.content.Context
|
||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
|
||||
interface AnalyticsManager {
|
||||
fun setup(context: Context, userProperties: Map<String, String>)
|
||||
fun sentEvent(event: Event)
|
||||
fun sentEvent(event: Analytics)
|
||||
}
|
||||
|
|
|
@ -2,13 +2,14 @@ package dev.lucasnlm.antimine.core.analytics
|
|||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import dev.lucasnlm.antimine.core.analytics.models.Analytics
|
||||
|
||||
class DebugAnalyticsManager : AnalyticsManager {
|
||||
override fun setup(context: Context, userProperties: Map<String, String>) {
|
||||
Log.d(TAG, "Setup Analytics using $userProperties")
|
||||
}
|
||||
|
||||
override fun sentEvent(event: Event) {
|
||||
override fun sentEvent(event: Analytics) {
|
||||
Log.d(TAG, "Sent event: '${event.title}' with ${event.extra}")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
package dev.lucasnlm.antimine.core.analytics
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.data.DifficultyPreset
|
||||
import dev.lucasnlm.antimine.common.level.data.GameStats
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
|
||||
sealed class Event(
|
||||
val title: String,
|
||||
val extra: Map<String, String> = mapOf()
|
||||
) {
|
||||
class Open : Event("Open game")
|
||||
|
||||
class NewGame(levelSetup: LevelSetup, seed: Long, useAccessibilityMode: Boolean) :
|
||||
Event("New Game", mapOf(
|
||||
"Seed" to seed.toString(),
|
||||
"Difficulty Preset" to levelSetup.preset.text,
|
||||
"Width" to levelSetup.width.toString(),
|
||||
"Height" to levelSetup.height.toString(),
|
||||
"Mines" to levelSetup.mines.toString(),
|
||||
"Accessibility" to useAccessibilityMode.toString()
|
||||
)
|
||||
)
|
||||
|
||||
class ResumePreviousGame : Event("Resume previous game")
|
||||
|
||||
class LongPressArea(index: Int) : Event("Long press area",
|
||||
mapOf("Index" to index.toString())
|
||||
)
|
||||
|
||||
class LongPressMultipleArea(index: Int) : Event("Long press to open multiple",
|
||||
mapOf("Index" to index.toString())
|
||||
)
|
||||
|
||||
class PressArea(index: Int) : Event("Press area",
|
||||
mapOf("Index" to index.toString())
|
||||
)
|
||||
|
||||
class GameOver(time: Long, gameStats: GameStats) : Event("Game Over",
|
||||
mapOf(
|
||||
"Time" to time.toString(),
|
||||
"Right Mines" to gameStats.rightMines.toString(),
|
||||
"Total Mines" to gameStats.totalMines.toString(),
|
||||
"Total Area" to gameStats.totalArea.toString()
|
||||
)
|
||||
)
|
||||
|
||||
class Victory(time: Long, gameStats: GameStats, difficultyPreset: DifficultyPreset) : Event(
|
||||
"Victory",
|
||||
mapOf(
|
||||
"Time" to time.toString(),
|
||||
"Difficulty" to difficultyPreset.text,
|
||||
"Right Mines" to gameStats.rightMines.toString(),
|
||||
"Total Mines" to gameStats.totalMines.toString(),
|
||||
"Total Area" to gameStats.totalArea.toString()
|
||||
)
|
||||
)
|
||||
|
||||
class Resume : Event("Back to the game")
|
||||
|
||||
class Quit : Event("Quit game")
|
||||
|
||||
class OpenDrawer : Event("Opened Drawer")
|
||||
|
||||
class CloseDrawer : Event("Closed Drawer")
|
||||
|
||||
class OpenAbout : Event("Open About")
|
||||
|
||||
class OpenSettings : Event("Open Settings")
|
||||
|
||||
class ShowRatingRequest(usages: Int) : Event("Shown Rating Request",
|
||||
mapOf(
|
||||
"Usages" to usages.toString()
|
||||
))
|
||||
|
||||
class TapRatingRequest(from: String) : Event("Rating Request",
|
||||
mapOf(
|
||||
"From" to from
|
||||
))
|
||||
|
||||
class TapGameReset(resign: Boolean) : Event("Game reset",
|
||||
mapOf("Resign" to resign.toString())
|
||||
)
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package dev.lucasnlm.antimine.core.analytics.models
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import dev.lucasnlm.antimine.common.level.models.Score
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
|
||||
sealed class Analytics(
|
||||
val title: String,
|
||||
val extra: Map<String, String> = mapOf()
|
||||
) {
|
||||
class Open : Analytics("Open game")
|
||||
|
||||
class NewGame(minefield: Minefield, difficulty: Difficulty, seed: Long, useAccessibilityMode: Boolean) :
|
||||
Analytics("New Game", mapOf(
|
||||
"Seed" to seed.toString(),
|
||||
"Difficulty Preset" to difficulty.text,
|
||||
"Width" to minefield.width.toString(),
|
||||
"Height" to minefield.height.toString(),
|
||||
"Mines" to minefield.mines.toString(),
|
||||
"Accessibility" to useAccessibilityMode.toString()
|
||||
)
|
||||
)
|
||||
|
||||
class ResumePreviousGame : Analytics("Resume previous game")
|
||||
|
||||
class LongPressArea(index: Int) : Analytics("Long press area",
|
||||
mapOf("Index" to index.toString())
|
||||
)
|
||||
|
||||
class LongPressMultipleArea(index: Int) : Analytics("Long press to open multiple",
|
||||
mapOf("Index" to index.toString())
|
||||
)
|
||||
|
||||
class PressArea(index: Int) : Analytics("Press area",
|
||||
mapOf("Index" to index.toString())
|
||||
)
|
||||
|
||||
class GameOver(time: Long, score: Score) : Analytics("Game Over",
|
||||
mapOf(
|
||||
"Time" to time.toString(),
|
||||
"Right Mines" to score.rightMines.toString(),
|
||||
"Total Mines" to score.totalMines.toString(),
|
||||
"Total Area" to score.totalArea.toString()
|
||||
)
|
||||
)
|
||||
|
||||
class Victory(time: Long, score: Score, difficulty: Difficulty) : Analytics(
|
||||
"Victory",
|
||||
mapOf(
|
||||
"Time" to time.toString(),
|
||||
"Difficulty" to difficulty.text,
|
||||
"Right Mines" to score.rightMines.toString(),
|
||||
"Total Mines" to score.totalMines.toString(),
|
||||
"Total Area" to score.totalArea.toString()
|
||||
)
|
||||
)
|
||||
|
||||
class Resume : Analytics("Back to the game")
|
||||
|
||||
class Quit : Analytics("Quit game")
|
||||
|
||||
class OpenDrawer : Analytics("Opened Drawer")
|
||||
|
||||
class CloseDrawer : Analytics("Closed Drawer")
|
||||
|
||||
class OpenAbout : Analytics("Open About")
|
||||
|
||||
class OpenSettings : Analytics("Open Settings")
|
||||
|
||||
class ShowRatingRequest(usages: Int) : Analytics("Shown Rating Request",
|
||||
mapOf(
|
||||
"Usages" to usages.toString()
|
||||
))
|
||||
|
||||
class TapRatingRequest(from: String) : Analytics("Rating Request",
|
||||
mapOf(
|
||||
"From" to from
|
||||
))
|
||||
|
||||
class TapGameReset(resign: Boolean) : Analytics("Game reset",
|
||||
mapOf("Resign" to resign.toString())
|
||||
)
|
||||
}
|
|
@ -2,7 +2,7 @@ package dev.lucasnlm.antimine.core.preferences
|
|||
|
||||
import android.app.Application
|
||||
import androidx.preference.PreferenceManager
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import javax.inject.Inject
|
||||
|
||||
class PreferencesInteractor @Inject constructor(
|
||||
|
@ -12,17 +12,17 @@ class PreferencesInteractor @Inject constructor(
|
|||
PreferenceManager.getDefaultSharedPreferences(application)
|
||||
}
|
||||
|
||||
fun getCustomMode() = LevelSetup(
|
||||
fun getCustomMode() = Minefield(
|
||||
preferences.getInt(PREFERENCE_CUSTOM_GAME_WIDTH, 9),
|
||||
preferences.getInt(PREFERENCE_CUSTOM_GAME_HEIGHT, 9),
|
||||
preferences.getInt(PREFERENCE_CUSTOM_GAME_MINES, 9)
|
||||
)
|
||||
|
||||
fun updateCustomMode(customLevelSetup: LevelSetup) {
|
||||
fun updateCustomMode(customMinefield: Minefield) {
|
||||
preferences.edit().apply {
|
||||
putInt(PREFERENCE_CUSTOM_GAME_WIDTH, customLevelSetup.width)
|
||||
putInt(PREFERENCE_CUSTOM_GAME_HEIGHT, customLevelSetup.height)
|
||||
putInt(PREFERENCE_CUSTOM_GAME_MINES, customLevelSetup.mines)
|
||||
putInt(PREFERENCE_CUSTOM_GAME_WIDTH, customMinefield.width)
|
||||
putInt(PREFERENCE_CUSTOM_GAME_HEIGHT, customMinefield.height)
|
||||
putInt(PREFERENCE_CUSTOM_GAME_MINES, customMinefield.mines)
|
||||
}.apply()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package dev.lucasnlm.antimine.core.preferences
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
|
||||
interface IPreferencesRepository {
|
||||
fun customGameMode(): LevelSetup
|
||||
fun updateCustomGameMode(levelSetup: LevelSetup)
|
||||
fun customGameMode(): Minefield
|
||||
fun updateCustomGameMode(minefield: Minefield)
|
||||
fun getBoolean(key: String, defaultValue: Boolean): Boolean
|
||||
fun getInt(key: String, defaultValue: Int): Int
|
||||
fun putBoolean(key: String, value: Boolean)
|
||||
|
@ -19,11 +19,11 @@ class PreferencesRepository(
|
|||
private val preferencesInteractor: PreferencesInteractor
|
||||
) : IPreferencesRepository {
|
||||
|
||||
override fun customGameMode(): LevelSetup =
|
||||
override fun customGameMode(): Minefield =
|
||||
preferencesInteractor.getCustomMode()
|
||||
|
||||
override fun updateCustomGameMode(levelSetup: LevelSetup) =
|
||||
preferencesInteractor.updateCustomMode(levelSetup)
|
||||
override fun updateCustomGameMode(minefield: Minefield) =
|
||||
preferencesInteractor.updateCustomMode(minefield)
|
||||
|
||||
override fun getBoolean(key: String, defaultValue: Boolean): Boolean =
|
||||
preferencesInteractor.getBoolean(key, defaultValue)
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package dev.lucasnlm.antimine.core.scope
|
||||
|
||||
import javax.inject.Scope
|
||||
|
||||
@Scope
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class FragmentScope
|
|
@ -4,7 +4,7 @@
|
|||
<string name="app_description">Deshabilitar las minas ocultas del campo de minas.</string>
|
||||
<string name="remaining_mines">Minas Restantes</string>
|
||||
<string name="install">Instalar</string>
|
||||
<string name="levelSetup">Dificultad</string>
|
||||
<string name="minefield">Dificultad</string>
|
||||
<string name="standard">Estándar</string>
|
||||
<string name="beginner">Principiante</string>
|
||||
<string name="intermediate">Intermediario</string>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<string name="app_description">Encontre todas as minas escondidas no campo minado.</string>
|
||||
<string name="remaining_mines">Minas Restantes</string>
|
||||
<string name="install">Instalar</string>
|
||||
<string name="levelSetup">Dificuldade</string>
|
||||
<string name="minefield">Dificuldade</string>
|
||||
<string name="standard">Padrão</string>
|
||||
<string name="beginner">Iniciante</string>
|
||||
<string name="intermediate">Intermediário</string>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<string name="app_description">你需要清除一个隐藏着地雷的矩形面板,不能使任何地雷爆炸。</string>
|
||||
<string name="remaining_mines">剩余地雷</string>
|
||||
<string name="install">安装</string>
|
||||
<string name="levelSetup">难度</string>
|
||||
<string name="minefield">难度</string>
|
||||
<string name="standard">标准</string>
|
||||
<string name="beginner">初级</string>
|
||||
<string name="intermediate">中级</string>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<string name="app_description">You have to clear a rectangular board containing hidden "mines" without detonating any of them.</string>
|
||||
<string name="remaining_mines">Remaining mines</string>
|
||||
<string name="install">Install</string>
|
||||
<string name="levelSetup">Difficulty</string>
|
||||
<string name="minefield">Difficulty</string>
|
||||
<string name="standard">Standard</string>
|
||||
<string name="beginner">Beginner</string>
|
||||
<string name="intermediate">Intermediate</string>
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package dev.lucasnlm.antimine.common.level
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.data.Area
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.data.Mark
|
||||
import dev.lucasnlm.antimine.common.level.data.isFlag
|
||||
import dev.lucasnlm.antimine.common.level.data.isQuestion
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotEquals
|
||||
|
@ -14,7 +12,7 @@ import org.junit.Test
|
|||
class LevelFacadeTest {
|
||||
|
||||
private fun levelFacadeOf(width: Int, height: Int, mines: Int, seed: Long = 0L) =
|
||||
LevelFacade(0, LevelSetup(width, height, mines), seed)
|
||||
LevelFacade(Minefield(width, height, mines), seed)
|
||||
|
||||
private fun LevelFacade.at(id: Int): Area = field.first { it.id == id }
|
||||
|
||||
|
|
|
@ -3,21 +3,22 @@ package dev.lucasnlm.antimine.common.level
|
|||
import android.util.DisplayMetrics
|
||||
import com.nhaarman.mockitokotlin2.doReturn
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import dev.lucasnlm.antimine.common.level.data.DifficultyPreset
|
||||
import dev.lucasnlm.antimine.common.level.data.LevelSetup
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import dev.lucasnlm.antimine.common.level.repository.MinefieldRepository
|
||||
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
|
||||
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class LevelSetupFactoryTest {
|
||||
class MinefieldFactoryTest {
|
||||
private val dimensionRepository: IDimensionRepository = mock()
|
||||
private val preferencesRepository: IPreferencesRepository = mock()
|
||||
|
||||
@Test
|
||||
fun testFromDifficultyPresetBeginner() {
|
||||
GameModeFactory.fromDifficultyPreset(
|
||||
DifficultyPreset.Beginner, dimensionRepository, preferencesRepository
|
||||
MinefieldRepository().fromDifficulty(
|
||||
Difficulty.Beginner, dimensionRepository, preferencesRepository
|
||||
).run {
|
||||
assertEquals(9, width)
|
||||
assertEquals(9, height)
|
||||
|
@ -27,8 +28,8 @@ class LevelSetupFactoryTest {
|
|||
|
||||
@Test
|
||||
fun testFromDifficultyPresetIntermediate() {
|
||||
GameModeFactory.fromDifficultyPreset(
|
||||
DifficultyPreset.Intermediate, dimensionRepository, preferencesRepository
|
||||
MinefieldRepository().fromDifficulty(
|
||||
Difficulty.Intermediate, dimensionRepository, preferencesRepository
|
||||
).run {
|
||||
assertEquals(16, width)
|
||||
assertEquals(16, height)
|
||||
|
@ -38,8 +39,8 @@ class LevelSetupFactoryTest {
|
|||
|
||||
@Test
|
||||
fun testFromDifficultyPresetExpert() {
|
||||
GameModeFactory.fromDifficultyPreset(
|
||||
DifficultyPreset.Expert, dimensionRepository, preferencesRepository
|
||||
MinefieldRepository().fromDifficulty(
|
||||
Difficulty.Expert, dimensionRepository, preferencesRepository
|
||||
).run {
|
||||
assertEquals(24, width)
|
||||
assertEquals(24, height)
|
||||
|
@ -50,11 +51,15 @@ class LevelSetupFactoryTest {
|
|||
@Test
|
||||
fun testFromDifficultyPresetCustom() {
|
||||
val preferencesRepository: IPreferencesRepository = mock {
|
||||
on { customGameMode() } doReturn LevelSetup(10, 10, 30, DifficultyPreset.Custom)
|
||||
on { customGameMode() } doReturn Minefield(
|
||||
10,
|
||||
10,
|
||||
30
|
||||
)
|
||||
}
|
||||
|
||||
GameModeFactory.fromDifficultyPreset(
|
||||
DifficultyPreset.Custom,
|
||||
MinefieldRepository().fromDifficulty(
|
||||
Difficulty.Custom,
|
||||
mock(),
|
||||
preferencesRepository
|
||||
).run {
|
||||
|
@ -75,8 +80,8 @@ class LevelSetupFactoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
GameModeFactory.fromDifficultyPreset(
|
||||
DifficultyPreset.Standard,
|
||||
MinefieldRepository().fromDifficulty(
|
||||
Difficulty.Standard,
|
||||
dimensionRepository,
|
||||
preferencesRepository
|
||||
).run {
|
|
@ -0,0 +1,32 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.converters
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.models.Difficulty
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
|
||||
class DifficultyConverterTest {
|
||||
@Test
|
||||
fun toDifficulty() {
|
||||
val converter = DifficultyConverter()
|
||||
assertEquals(converter.toDifficulty(0), Difficulty.Standard)
|
||||
assertEquals(converter.toDifficulty(1), Difficulty.Beginner)
|
||||
assertEquals(converter.toDifficulty(2), Difficulty.Intermediate)
|
||||
assertEquals(converter.toDifficulty(3), Difficulty.Expert)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun toDifficultyInvalid() {
|
||||
val converter = DifficultyConverter()
|
||||
converter.toDifficulty(5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toInteger() {
|
||||
val converter = DifficultyConverter()
|
||||
assertEquals(converter.toInteger(Difficulty.Standard), 0)
|
||||
assertEquals(converter.toInteger(Difficulty.Beginner), 1)
|
||||
assertEquals(converter.toInteger(Difficulty.Intermediate), 2)
|
||||
assertEquals(converter.toInteger(Difficulty.Expert), 3)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.converters
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.models.Area
|
||||
import dev.lucasnlm.antimine.common.level.models.Mark
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
|
||||
class FieldConverterTest {
|
||||
private val expectedJson =
|
||||
"[{\"id\":1,\"posX\":2,\"posY\":3,\"minesAround\":5,\"safeZone\":false,\"hasMine\":false,\"mistake\":false," +
|
||||
"\"isCovered\":true,\"mark\":\"None\",\"highlighted\":true},{\"id\":2,\"posX\":5,\"posY\":3," +
|
||||
"\"minesAround\":0,\"safeZone\":true,\"hasMine\":true,\"mistake\":true,\"isCovered\":false," +
|
||||
"\"mark\":\"PurposefulNone\",\"highlighted\":false},{\"id\":3,\"posX\":1,\"posY\":1," +
|
||||
"\"minesAround\":3,\"safeZone\":true,\"hasMine\":true,\"mistake\":false,\"isCovered\":true," +
|
||||
"\"mark\":\"Flag\",\"highlighted\":true},{\"id\":4,\"posX\":0,\"posY\":0,\"minesAround\":6," +
|
||||
"\"safeZone\":false,\"hasMine\":false,\"mistake\":false,\"isCovered\":true," +
|
||||
"\"mark\":\"Question\",\"highlighted\":true}]"
|
||||
|
||||
private val areaList =
|
||||
listOf(
|
||||
Area(
|
||||
1, 2, 3, 5,
|
||||
safeZone = false,
|
||||
hasMine = false,
|
||||
mistake = false,
|
||||
isCovered = true,
|
||||
mark = Mark.None,
|
||||
highlighted = true
|
||||
),
|
||||
Area(
|
||||
2, 5, 3, 0,
|
||||
safeZone = true,
|
||||
hasMine = true,
|
||||
mistake = true,
|
||||
isCovered = false,
|
||||
mark = Mark.PurposefulNone,
|
||||
highlighted = false
|
||||
),
|
||||
Area(
|
||||
3, 1, 1, 3,
|
||||
safeZone = true,
|
||||
hasMine = true,
|
||||
mistake = false,
|
||||
isCovered = true,
|
||||
mark = Mark.Flag,
|
||||
highlighted = true
|
||||
),
|
||||
Area(
|
||||
4, 0, 0, 6,
|
||||
safeZone = false,
|
||||
hasMine = false,
|
||||
mistake = false,
|
||||
isCovered = true,
|
||||
mark = Mark.Question,
|
||||
highlighted = true
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun toAreaList() {
|
||||
val fieldConverter = FieldConverter()
|
||||
val list = fieldConverter.toAreaList(expectedJson)
|
||||
assertEquals(
|
||||
areaList, list
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toJsonString() {
|
||||
val fieldConverter = FieldConverter()
|
||||
val result = fieldConverter.toJsonString(areaList)
|
||||
assertEquals(expectedJson, result)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.converters
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.models.Minefield
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
|
||||
class MinefieldConverterTest {
|
||||
private val expectedJson = "{\"width\":3,\"height\":3,\"mines\":4}"
|
||||
private val expectedMinefield = Minefield(3, 3, 4)
|
||||
|
||||
@Test
|
||||
fun toMinefield() {
|
||||
val converter = MinefieldConverter()
|
||||
val minefield = converter.toMinefield(expectedJson)
|
||||
assertEquals(expectedMinefield, minefield)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toJsonString() {
|
||||
val converter = MinefieldConverter()
|
||||
val json = converter.toJsonString(expectedMinefield)
|
||||
assertEquals(expectedJson, json)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package dev.lucasnlm.antimine.common.level.database.converters
|
||||
|
||||
import dev.lucasnlm.antimine.common.level.database.models.SaveStatus
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
|
||||
class SaveStatusConverterTest {
|
||||
@Test
|
||||
fun toSaveStatus() {
|
||||
val converter = SaveStatusConverter()
|
||||
assertEquals(SaveStatus.ON_GOING, converter.toSaveStatus(0))
|
||||
assertEquals(SaveStatus.VICTORY, converter.toSaveStatus(1))
|
||||
assertEquals(SaveStatus.DEFEAT, converter.toSaveStatus(2))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException::class)
|
||||
fun toSaveStatusInvalid() {
|
||||
val converter = SaveStatusConverter()
|
||||
converter.toSaveStatus(5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun toInteger() {
|
||||
val converter = SaveStatusConverter()
|
||||
assertEquals(0, converter.toInteger(SaveStatus.ON_GOING))
|
||||
assertEquals(1, converter.toInteger(SaveStatus.VICTORY))
|
||||
assertEquals(2, converter.toInteger(SaveStatus.DEFEAT))
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import androidx.lifecycle.Observer
|
|||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.wear.widget.SwipeDismissFrameLayout
|
||||
import dagger.android.support.DaggerAppCompatActivity
|
||||
import dev.lucasnlm.antimine.common.level.data.GameStatus
|
||||
import dev.lucasnlm.antimine.common.level.models.Status
|
||||
import dev.lucasnlm.antimine.common.level.utils.Clock
|
||||
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
|
||||
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModelFactory
|
||||
|
@ -22,8 +22,8 @@ import androidx.wear.ambient.AmbientModeSupport
|
|||
import androidx.wear.ambient.AmbientModeSupport.AmbientCallback
|
||||
import androidx.wear.ambient.AmbientModeSupport.EXTRA_LOWBIT_AMBIENT
|
||||
import dev.lucasnlm.antimine.R
|
||||
import dev.lucasnlm.antimine.common.level.data.AmbientSettings
|
||||
import dev.lucasnlm.antimine.common.level.data.GameEvent
|
||||
import dev.lucasnlm.antimine.common.level.models.AmbientSettings
|
||||
import dev.lucasnlm.antimine.common.level.models.Event
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -43,7 +43,7 @@ class WatchGameActivity : DaggerAppCompatActivity(), AmbientModeSupport.AmbientC
|
|||
|
||||
private val clock = Clock()
|
||||
private var lastShownTime: String? = null
|
||||
private var gameStatus: GameStatus = GameStatus.PreGame
|
||||
private var status: Status = Status.PreGame
|
||||
|
||||
private var ambientMode: AmbientCallback = object : AmbientCallback() {
|
||||
override fun onExitAmbient() {
|
||||
|
@ -59,7 +59,12 @@ class WatchGameActivity : DaggerAppCompatActivity(), AmbientModeSupport.AmbientC
|
|||
override fun onEnterAmbient(ambientDetails: Bundle?) {
|
||||
super.onEnterAmbient(ambientDetails)
|
||||
val lowBit = ambientDetails?.getBoolean(EXTRA_LOWBIT_AMBIENT) ?: true
|
||||
currentLevelFragment?.setAmbientMode(AmbientSettings(true, lowBit))
|
||||
currentLevelFragment?.setAmbientMode(
|
||||
AmbientSettings(
|
||||
true,
|
||||
lowBit
|
||||
)
|
||||
)
|
||||
updateClockText(true)
|
||||
}
|
||||
}
|
||||
|
@ -149,35 +154,35 @@ class WatchGameActivity : DaggerAppCompatActivity(), AmbientModeSupport.AmbientC
|
|||
})
|
||||
}
|
||||
|
||||
private fun onGameEvent(event: GameEvent) {
|
||||
private fun onGameEvent(event: Event) {
|
||||
when (event) {
|
||||
GameEvent.StartNewGame -> {
|
||||
gameStatus = GameStatus.PreGame
|
||||
Event.StartNewGame -> {
|
||||
status = Status.PreGame
|
||||
}
|
||||
GameEvent.Resume, GameEvent.Running -> {
|
||||
gameStatus = GameStatus.Running
|
||||
Event.Resume, Event.Running -> {
|
||||
status = Status.Running
|
||||
}
|
||||
GameEvent.Victory -> {
|
||||
gameStatus = GameStatus.Over()
|
||||
Event.Victory -> {
|
||||
status = Status.Over()
|
||||
|
||||
messageText.text = getString(R.string.victory)
|
||||
waitAndShowNewGameButton()
|
||||
}
|
||||
GameEvent.GameOver -> {
|
||||
gameStatus = GameStatus.Over()
|
||||
Event.GameOver -> {
|
||||
status = Status.Over()
|
||||
viewModel.stopClock()
|
||||
viewModel.gameOver()
|
||||
|
||||
messageText.text = getString(R.string.game_over)
|
||||
waitAndShowNewGameButton()
|
||||
}
|
||||
GameEvent.ResumeVictory -> {
|
||||
gameStatus = GameStatus.Over()
|
||||
Event.ResumeVictory -> {
|
||||
status = Status.Over()
|
||||
messageText.text = getString(R.string.victory)
|
||||
waitAndShowNewGameButton(0L)
|
||||
}
|
||||
GameEvent.ResumeGameOver -> {
|
||||
gameStatus = GameStatus.Over()
|
||||
Event.ResumeGameOver -> {
|
||||
status = Status.Over()
|
||||
messageText.text = getString(R.string.game_over)
|
||||
waitAndShowNewGameButton(0L)
|
||||
}
|
||||
|
@ -188,7 +193,7 @@ class WatchGameActivity : DaggerAppCompatActivity(), AmbientModeSupport.AmbientC
|
|||
|
||||
private fun waitAndShowNewGameButton(wait: Long = DateUtils.SECOND_IN_MILLIS) {
|
||||
HandlerCompat.postDelayed(Handler(), {
|
||||
if (this.gameStatus is GameStatus.Over && !isFinishing) {
|
||||
if (this.status is Status.Over && !isFinishing) {
|
||||
newGame.visibility = View.VISIBLE
|
||||
newGame.setOnClickListener {
|
||||
it.visibility = View.GONE
|
||||
|
|
|
@ -10,8 +10,8 @@ import androidx.recyclerview.widget.GridLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dev.lucasnlm.antimine.common.R
|
||||
import dagger.android.support.DaggerFragment
|
||||
import dev.lucasnlm.antimine.common.level.data.AmbientSettings
|
||||
import dev.lucasnlm.antimine.common.level.data.GameEvent
|
||||
import dev.lucasnlm.antimine.common.level.models.AmbientSettings
|
||||
import dev.lucasnlm.antimine.common.level.models.Event
|
||||
import dev.lucasnlm.antimine.common.level.view.AreaAdapter
|
||||
import dev.lucasnlm.antimine.common.level.view.UnlockedHorizontalScrollView
|
||||
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
|
||||
|
@ -98,13 +98,13 @@ class WatchLevelFragment : DaggerFragment() {
|
|||
areaAdapter.notifyItemChanged(it)
|
||||
})
|
||||
eventObserver.observe(viewLifecycleOwner, Observer {
|
||||
if (it == GameEvent.StartNewGame) {
|
||||
if (it == Event.StartNewGame) {
|
||||
recyclerGrid.scrollToPosition(areaAdapter.itemCount / 2)
|
||||
}
|
||||
|
||||
when (it) {
|
||||
GameEvent.ResumeGameOver, GameEvent.GameOver,
|
||||
GameEvent.Victory, GameEvent.ResumeVictory -> areaAdapter.setClickEnabled(false)
|
||||
Event.ResumeGameOver, Event.GameOver,
|
||||
Event.Victory, Event.ResumeVictory -> areaAdapter.setClickEnabled(false)
|
||||
else -> areaAdapter.setClickEnabled(true)
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue