Merge pull request #17 from lucasnlm/move-classes

Move classes
This commit is contained in:
Lucas Nunes 2020-03-18 00:37:17 -03:00 committed by GitHub
commit 026957bca4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 990 additions and 577 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,

View file

@ -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,

View file

@ -1,4 +1,4 @@
package dev.lucasnlm.antimine.common.level.data
package dev.lucasnlm.antimine.common.level.models
data class Area(
val id: Int,

View file

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

View file

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

View file

@ -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"),

View file

@ -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,

View file

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

View file

@ -0,0 +1,7 @@
package dev.lucasnlm.antimine.common.level.models
data class Minefield(
val width: Int,
val height: Int,
val mines: Int
)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +0,0 @@
package dev.lucasnlm.antimine.core.scope
import javax.inject.Scope
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class FragmentScope

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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