Merge pull request #126 from lucasnlm/fix-bugs

Fix bugs
This commit is contained in:
Lucas Nunes 2020-07-05 21:47:56 -03:00 committed by GitHub
commit 27cab25b31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 348 additions and 200 deletions

View file

@ -9,8 +9,8 @@ android {
defaultConfig {
// versionCode and versionName must be hardcoded to support F-droid
versionCode 702031
versionName '7.2.3'
versionCode 702041
versionName '7.2.4'
minSdkVersion 16
targetSdkVersion 30
multiDexEnabled true
@ -88,7 +88,7 @@ dependencies {
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.activity:activity-ktx:1.1.0'
implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation 'androidx.fragment:fragment-ktx:1.2.5'
// Constraint
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

View file

@ -105,6 +105,22 @@ class GameActivity : AppCompatActivity(), DialogInterface.OnDismissListener {
}
)
retryObserver.observe(
this@GameActivity,
Observer {
GlobalScope.launch {
viewModel.retryGame(currentSaveId.toInt())
}
}
)
shareObserver.observe(
this@GameActivity,
Observer {
shareCurrentGame()
}
)
elapsedTimeSeconds.observe(
this@GameActivity,
Observer {
@ -445,8 +461,7 @@ class GameActivity : AppCompatActivity(), DialogInterface.OnDismissListener {
victory,
score?.rightMines ?: 0,
score?.totalMines ?: 0,
currentGameStatus.time,
currentSaveId
currentGameStatus.time
).apply {
showAllowingStateLoss(supportFragmentManager, EndGameDialogFragment.TAG)
}
@ -516,6 +531,7 @@ class GameActivity : AppCompatActivity(), DialogInterface.OnDismissListener {
)
}
Event.GameOver -> {
val isResuming = (status == Status.PreGame)
val score = Score(
rightMines,
totalMines,
@ -527,7 +543,7 @@ class GameActivity : AppCompatActivity(), DialogInterface.OnDismissListener {
viewModel.stopClock()
GlobalScope.launch(context = Dispatchers.Main) {
viewModel.gameOver()
viewModel.gameOver(isResuming)
waitAndShowEndGameDialog(
victory = false,
await = true

View file

@ -309,7 +309,7 @@ class TvGameActivity : AppCompatActivity() {
viewModel.stopClock()
GlobalScope.launch(context = Dispatchers.Main) {
viewModel.gameOver()
viewModel.gameOver(false)
waitAndShowGameOverConfirmNewGame()
}
}

View file

@ -12,7 +12,8 @@ class AboutViewModel : ViewModel() {
fun getTranslatorsList() = mapOf(
"Arabic" to sequenceOf("Ahmad Alkurbi"),
"Chinese Simplified" to sequenceOf("linsui"),
"Chinese Simplified" to sequenceOf("linsui", "yilinzhao2020"),
"Catalan" to sequenceOf("dmanye", "Archison"),
"Czech" to sequenceOf("novas78@xda"),
"Dutch" to sequenceOf("Max Pietersma"),
"English" to sequenceOf("miguelsouza2212"),

View file

@ -14,9 +14,6 @@ import dev.lucasnlm.antimine.R
import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel
import dev.lucasnlm.antimine.instant.InstantAppManager
import dev.lucasnlm.antimine.level.viewmodel.EngGameDialogViewModel
import dev.lucasnlm.antimine.share.viewmodel.ShareViewModel
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import javax.inject.Inject
@AndroidEntryPoint
@ -26,14 +23,12 @@ class EndGameDialogFragment : AppCompatDialogFragment() {
private val endGameViewModel by activityViewModels<EngGameDialogViewModel>()
private val viewModel by activityViewModels<GameViewModel>()
private val shareViewModel by activityViewModels<ShareViewModel>()
private var hasValidData = false
private var isVictory: Boolean = false
private var time: Long = 0L
private var rightMines: Int = 0
private var totalMines: Int = 0
private var saveId: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -44,7 +39,6 @@ class EndGameDialogFragment : AppCompatDialogFragment() {
rightMines = getInt(DIALOG_RIGHT_MINES, 0)
totalMines = getInt(DIALOG_TOTAL_MINES, 0)
hasValidData = (totalMines > 0)
saveId = getLong(DIALOG_SAVE_ID, 0L)
}
}
@ -100,9 +94,7 @@ class EndGameDialogFragment : AppCompatDialogFragment() {
setView(view)
setPositiveButton(R.string.new_game) { _, _ ->
GlobalScope.launch {
viewModel.startNewGame()
}
viewModel.startNewGame()
}
when {
@ -115,33 +107,25 @@ class EndGameDialogFragment : AppCompatDialogFragment() {
}
isVictory -> {
setNeutralButton(R.string.share) { _, _ ->
val setup = viewModel.levelSetup.value
val field = viewModel.field.value
GlobalScope.launch {
shareViewModel.share(setup, field)
}
viewModel.shareObserver.postValue(Unit)
}
}
else -> {
setNeutralButton(R.string.retry) { _, _ ->
GlobalScope.launch {
viewModel.retryGame(saveId.toInt())
}
viewModel.retryObserver.postValue(Unit)
}
}
}
}.create()
companion object {
fun newInstance(victory: Boolean, rightMines: Int, totalMines: Int, time: Long, saveId: Long) =
fun newInstance(victory: Boolean, rightMines: Int, totalMines: Int, time: Long) =
EndGameDialogFragment().apply {
arguments = Bundle().apply {
putBoolean(DIALOG_IS_VICTORY, victory)
putInt(DIALOG_RIGHT_MINES, rightMines)
putInt(DIALOG_TOTAL_MINES, totalMines)
putLong(DIALOG_TIME, time)
putLong(DIALOG_SAVE_ID, saveId)
}
}
@ -149,7 +133,6 @@ class EndGameDialogFragment : AppCompatDialogFragment() {
private const val DIALOG_TIME = "dialog_time"
private const val DIALOG_RIGHT_MINES = "dialog_right_mines"
private const val DIALOG_TOTAL_MINES = "dialog_total_mines"
private const val DIALOG_SAVE_ID = "dialog_save_id"
val TAG = EndGameDialogFragment::class.simpleName!!
}

View file

@ -13,8 +13,8 @@ import dev.lucasnlm.antimine.common.level.repository.IStatsRepository
import dev.lucasnlm.antimine.common.level.repository.MemorySavesRepository
import dev.lucasnlm.antimine.common.level.repository.MemoryStatsRepository
import dev.lucasnlm.antimine.common.level.utils.Clock
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
import dev.lucasnlm.antimine.mocks.DisabledHapticFeedbackInteractor
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager
import dev.lucasnlm.antimine.mocks.DisabledHapticFeedbackManager
import dev.lucasnlm.antimine.mocks.FixedDimensionRepository
import dev.lucasnlm.antimine.mocks.FixedMinefieldRepository
@ -40,5 +40,5 @@ class TestLevelModule {
fun provideMinefieldRepository(): IMinefieldRepository = FixedMinefieldRepository()
@Provides
fun provideHapticFeedbackInteractor(): IHapticFeedbackInteractor = DisabledHapticFeedbackInteractor()
fun provideHapticFeedbackInteractor(): IHapticFeedbackManager = DisabledHapticFeedbackManager()
}

View file

@ -1,8 +1,8 @@
package dev.lucasnlm.antimine.mocks
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager
class DisabledHapticFeedbackInteractor : IHapticFeedbackInteractor {
class DisabledHapticFeedbackManager : IHapticFeedbackManager {
override fun longPressFeedback() {
// Empty
}

View file

@ -9,8 +9,8 @@ android {
defaultConfig {
// versionCode and versionName must be hardcoded to support F-droid
versionCode 702031
versionName '7.2.3'
versionCode 702041
versionName '7.2.4'
minSdkVersion 16
targetSdkVersion 30
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View file

@ -1,7 +1,6 @@
package dev.lucasnlm.antimine.common.level.di
import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.room.Room
import dagger.Module
import dagger.Provides
@ -11,7 +10,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import dev.lucasnlm.antimine.common.level.database.AppDataBase
import dev.lucasnlm.antimine.common.level.database.dao.SaveDao
import dev.lucasnlm.antimine.common.level.database.dao.StatsDao
import dev.lucasnlm.antimine.common.level.models.Event
import dev.lucasnlm.antimine.common.level.repository.DimensionRepository
import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository
import dev.lucasnlm.antimine.common.level.repository.IMinefieldRepository
@ -21,8 +19,8 @@ import dev.lucasnlm.antimine.common.level.repository.MinefieldRepository
import dev.lucasnlm.antimine.common.level.repository.SavesRepository
import dev.lucasnlm.antimine.common.level.repository.StatsRepository
import dev.lucasnlm.antimine.common.level.utils.Clock
import dev.lucasnlm.antimine.common.level.utils.HapticFeedbackInteractor
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
import dev.lucasnlm.antimine.common.level.utils.HapticFeedbackManager
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
@Module
@ -47,9 +45,6 @@ open class LevelModule {
appDataBase: AppDataBase
): StatsDao = appDataBase.statsDao()
@Provides
open fun provideGameEventObserver(): MutableLiveData<Event> = MutableLiveData()
@Provides
open fun provideClock(): Clock = Clock()
@ -75,10 +70,8 @@ open class LevelModule {
@Provides
open fun provideHapticFeedbackInteractor(
@ApplicationContext context: Context,
preferencesRepository: IPreferencesRepository
): IHapticFeedbackInteractor =
HapticFeedbackInteractor(context, preferencesRepository)
@ApplicationContext context: Context
): IHapticFeedbackManager = HapticFeedbackManager(context)
companion object {
private const val DATA_BASE_NAME = "saves-db"

View file

@ -3,7 +3,9 @@ package dev.lucasnlm.antimine.common.level.models
enum class Event {
StartNewGame,
ResumeGame,
@Deprecated("Use GameOver")
ResumeGameOver,
@Deprecated("Use Victory")
ResumeVictory,
Pause,
Resume,

View file

@ -1,54 +0,0 @@
package dev.lucasnlm.antimine.common.level.utils
import android.content.Context
import android.content.Context.VIBRATOR_SERVICE
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
interface IHapticFeedbackInteractor {
fun longPressFeedback()
fun explosionFeedback()
}
class HapticFeedbackInteractor(
context: Context,
private val preferencesRepository: IPreferencesRepository
) : IHapticFeedbackInteractor {
private val vibrator: Vibrator = context.getSystemService(VIBRATOR_SERVICE) as Vibrator
override fun longPressFeedback() {
if (preferencesRepository.useHapticFeedback()) {
vibrateTo(70, 240)
vibrateTo(10, 100)
}
}
override fun explosionFeedback() {
if (preferencesRepository.useHapticFeedback()) {
vibrateTo(400, -1)
}
}
private fun vibrateTo(time: Long, amplitude: Int) {
if (Build.VERSION.SDK_INT >= 26) {
vibrator.vibrate(
VibrationEffect.createOneShot(time, amplitude)
)
} else {
@Suppress("DEPRECATION")
vibrator.vibrate(time)
}
}
}
class DisabledIHapticFeedbackInteractor : IHapticFeedbackInteractor {
override fun longPressFeedback() {
// Empty
}
override fun explosionFeedback() {
// Empty
}
}

View file

@ -0,0 +1,39 @@
package dev.lucasnlm.antimine.common.level.utils
import android.content.Context
import android.content.Context.VIBRATOR_SERVICE
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
interface IHapticFeedbackManager {
fun longPressFeedback()
fun explosionFeedback()
}
class HapticFeedbackManager(
context: Context
) : IHapticFeedbackManager {
private val vibrator by lazy { context.getSystemService(VIBRATOR_SERVICE) as Vibrator }
override fun longPressFeedback() {
vibrateTo(70, 240)
vibrateTo(10, 100)
}
override fun explosionFeedback() {
vibrateTo(400, -1)
}
private fun vibrateTo(time: Long, amplitude: Int) {
if (Build.VERSION.SDK_INT >= 26) {
vibrator.vibrate(
VibrationEffect.createOneShot(time, amplitude)
)
} else {
@Suppress("DEPRECATION")
vibrator.vibrate(time)
}
}
}

View file

@ -1,12 +1,10 @@
package dev.lucasnlm.antimine.common.level.view
import android.content.Context
import android.os.Bundle
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.RecyclerView
@ -19,6 +17,7 @@ import javax.inject.Inject
abstract class CommonLevelFragment : Fragment() {
@Inject
lateinit var dimensionRepository: IDimensionRepository
@Inject
lateinit var preferencesRepository: IPreferencesRepository
@ -40,9 +39,8 @@ abstract class CommonLevelFragment : Fragment() {
}
protected fun calcHorizontalPadding(boardWidth: Int): Int {
val windowManager = requireContext().getSystemService(Context.WINDOW_SERVICE) as WindowManager
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
val context = requireContext()
val displayMetrics = context.resources.displayMetrics
val width = displayMetrics.widthPixels
val recyclerViewWidth = (dimensionRepository.areaSize() * boardWidth)
@ -52,14 +50,25 @@ abstract class CommonLevelFragment : Fragment() {
protected fun calcVerticalPadding(boardHeight: Int): Int {
val context = requireContext()
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
val displayMetrics = context.resources.displayMetrics
val height = recyclerGrid.measuredHeight
val typedValue = TypedValue()
val actionBarHeight = if (context.theme.resolveAttribute(android.R.attr.actionBarSize, typedValue, true)) {
TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)
} else {
0
}
val resourceId: Int = resources.getIdentifier("navigation_bar_height", "dimen", "android")
val navigationHeight = if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else 0
val height = displayMetrics.heightPixels
val recyclerViewHeight = (dimensionRepository.areaSize() * boardHeight)
val separatorsHeight = (2 * dimensionRepository.areaSeparator() * (boardHeight - 1))
return ((height - recyclerViewHeight - separatorsHeight) / 2).coerceAtLeast(0.0f).toInt()
val calculatedHeight = (height - actionBarHeight - navigationHeight - recyclerViewHeight - separatorsHeight)
return (calculatedHeight / 2).coerceAtLeast(0.0f).toInt()
}
}

View file

@ -4,6 +4,7 @@ import android.os.Handler
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import dev.lucasnlm.antimine.common.R
import dev.lucasnlm.antimine.common.level.GameController
import dev.lucasnlm.antimine.common.level.database.models.FirstOpen
import dev.lucasnlm.antimine.common.level.models.Area
@ -16,13 +17,14 @@ import dev.lucasnlm.antimine.common.level.repository.IMinefieldRepository
import dev.lucasnlm.antimine.common.level.repository.ISavesRepository
import dev.lucasnlm.antimine.common.level.repository.IStatsRepository
import dev.lucasnlm.antimine.common.level.utils.Clock
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager
import dev.lucasnlm.antimine.core.analytics.AnalyticsManager
import dev.lucasnlm.antimine.core.analytics.models.Analytics
import dev.lucasnlm.antimine.core.control.ActionResponse
import dev.lucasnlm.antimine.core.control.ActionFeedback
import dev.lucasnlm.antimine.core.control.GameControl
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.core.sound.ISoundManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
@ -30,16 +32,20 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class GameViewModel @ViewModelInject constructor(
val eventObserver: MutableLiveData<Event>,
private val savesRepository: ISavesRepository,
private val statsRepository: IStatsRepository,
private val dimensionRepository: IDimensionRepository,
private val preferencesRepository: IPreferencesRepository,
private val hapticFeedbackInteractor: IHapticFeedbackInteractor,
private val hapticFeedbackManager: IHapticFeedbackManager,
private val soundManager: ISoundManager,
private val minefieldRepository: IMinefieldRepository,
private val analyticsManager: AnalyticsManager,
private val clock: Clock
) : ViewModel() {
val eventObserver = MutableLiveData<Event>()
val retryObserver = MutableLiveData<Unit>()
val shareObserver = MutableLiveData<Unit>()
private lateinit var gameController: GameController
private var currentDifficulty: Difficulty = Difficulty.Standard
private var initialized = false
@ -118,8 +124,8 @@ class GameViewModel @ViewModelInject constructor(
plantMinesExcept(save.firstOpen.value, true)
singleClick(save.firstOpen.value)
}
refreshUserPreferences()
}
refreshUserPreferences()
mineCount.postValue(setup.mines)
difficulty.postValue(save.difficulty)
@ -234,7 +240,9 @@ class GameViewModel @ViewModelInject constructor(
onFeedbackAnalytics(feedback)
onPostAction()
hapticFeedbackInteractor.longPressFeedback()
if (preferencesRepository.useHapticFeedback()) {
hapticFeedbackManager.longPressFeedback()
}
}
fun onSingleClick(index: Int) {
@ -278,7 +286,6 @@ class GameViewModel @ViewModelInject constructor(
private fun updateGameState() {
when {
gameController.hasAnyMineExploded() -> {
hapticFeedbackInteractor.explosionFeedback()
eventObserver.postValue(Event.GameOver)
}
else -> {
@ -322,12 +329,22 @@ class GameViewModel @ViewModelInject constructor(
fun explosionDelay() = if (preferencesRepository.useAnimations()) 750L else 0L
suspend fun gameOver() {
suspend fun gameOver(fromResumeGame: Boolean) {
gameController.run {
analyticsManager.sentEvent(Analytics.GameOver(clock.time(), getScore()))
val explosionTime = (explosionDelay() / gameController.getMinesCount().coerceAtLeast(10))
val delayMillis = explosionTime.coerceAtLeast(25L)
if (!fromResumeGame) {
if (preferencesRepository.useHapticFeedback()) {
hapticFeedbackManager.explosionFeedback()
}
if (preferencesRepository.isSoundEffectsEnabled()) {
soundManager.play(R.raw.mine_explosion_sound)
}
}
findExplodedMine()?.let { exploded ->
takeExplosionRadius(exploded).forEach {
it.isCovered = false

View file

@ -11,24 +11,27 @@ import dev.lucasnlm.antimine.core.analytics.DebugAnalyticsManager
import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository
import dev.lucasnlm.antimine.core.preferences.PreferencesManager
import dev.lucasnlm.antimine.core.preferences.PreferencesRepository
import javax.inject.Singleton
import dev.lucasnlm.antimine.core.sound.ISoundManager
import dev.lucasnlm.antimine.core.sound.SoundManager
@Module
@InstallIn(ApplicationComponent::class)
class CommonModule {
@Singleton
@Provides
fun providePreferencesRepository(
preferencesManager: PreferencesManager
): IPreferencesRepository = PreferencesRepository(preferencesManager)
@Singleton
@Provides
fun providePreferencesInteractor(
@ApplicationContext context: Context
): PreferencesManager = PreferencesManager(context)
@Singleton
@Provides
fun provideAnalyticsManager(): AnalyticsManager = DebugAnalyticsManager()
@Provides
fun provideSoundManager(
@ApplicationContext context: Context
): ISoundManager = SoundManager(context)
}

View file

@ -20,6 +20,7 @@ interface IPreferencesRepository {
fun useLargeAreas(): Boolean
fun useAnimations(): Boolean
fun useQuestionMark(): Boolean
fun isSoundEffectsEnabled(): Boolean
}
class PreferencesRepository(
@ -68,7 +69,10 @@ class PreferencesRepository(
getBoolean(PREFERENCE_ANIMATION, true)
override fun useQuestionMark(): Boolean =
getBoolean(PREFERENCE_QUESTION_MARK, true)
getBoolean(PREFERENCE_QUESTION_MARK, false)
override fun isSoundEffectsEnabled(): Boolean =
getBoolean(PREFERENCE_SOUND_EFFECTS, false)
override fun controlStyle(): ControlStyle {
val index = getInt(PREFERENCE_CONTROL_STYLE, -1)
@ -100,5 +104,6 @@ class PreferencesRepository(
private const val PREFERENCE_CUSTOM_GAME_WIDTH = "preference_custom_game_width"
private const val PREFERENCE_CUSTOM_GAME_HEIGHT = "preference_custom_game_height"
private const val PREFERENCE_CUSTOM_GAME_MINES = "preference_custom_game_mines"
private const val PREFERENCE_SOUND_EFFECTS = "preference_sound"
}
}

View file

@ -0,0 +1,23 @@
package dev.lucasnlm.antimine.core.sound
import android.content.Context
import android.media.MediaPlayer
import androidx.annotation.RawRes
interface ISoundManager {
fun play(@RawRes soundId: Int)
}
class SoundManager(
private val context: Context
) : ISoundManager {
override fun play(@RawRes soundId: Int) {
MediaPlayer.create(context, soundId).apply {
setVolume(0.7f, 0.7f)
setOnCompletionListener {
release()
}
}.start()
}
}

View file

@ -1,71 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:locale="ca">
<string name="app_name">Antimine</string>
<string name="app_description">You have to clear a rectangular board containing hidden mines without detonating any of them.</string>
<string name="games">Games</string>
<string name="previous_games">Previous Games</string>
<string name="minefield">Difficulty</string>
<string name="standard">Standard</string>
<string name="beginner">Beginner</string>
<string name="intermediate">Intermediate</string>
<string name="app_name">Antimines</string>
<string name="app_description">Has de netejar un tauler rectangular amb mines amagades sense detonar-ne cap.</string>
<string name="games">Jocs</string>
<string name="previous_games">Partides prèvies</string>
<string name="minefield">Camp de mines</string>
<string name="standard">Estàndard</string>
<string name="beginner">Principiant</string>
<string name="intermediate">Intermedi</string>
<string name="expert">Expert</string>
<string name="open">Open</string>
<string name="settings">Settings</string>
<string name="animations">Animations</string>
<string name="vibration">Haptic Feedback</string>
<string name="about">About</string>
<string name="events">Statistics</string>
<string name="custom">Custom</string>
<string name="start">Start</string>
<string name="width">Width</string>
<string name="height">Height</string>
<string name="open">Obert</string>
<string name="settings">Configuració</string>
<string name="animations">Animacions</string>
<string name="vibration">Resposta hàptica</string>
<string name="about">Quant a</string>
<string name="events">Estadístiques</string>
<string name="custom">Personalitzat</string>
<string name="start">Inicia</string>
<string name="width">Amplada</string>
<string name="height">Alçada</string>
<string name="mines">Mines</string>
<string name="retry_sure">If you start a new game, your current progress will be lost.</string>
<string name="show_licenses">Show Licenses</string>
<string name="new_game_request">Do you want to start a new game?</string>
<string name="retry_sure">Si comences un altre joc, el teu progrés actual es perdrà.</string>
<string name="show_licenses">Mostra les llicències</string>
<string name="new_game_request">Vols començar un joc nou?</string>
<string name="mines_remaining">%d mines</string>
<string name="game_time">Game Time</string>
<string name="mine">Mine</string>
<string name="game_time">Temps de joc</string>
<string name="mine">Mina</string>
<string name="settings_general">General</string>
<string name="settings_gameplay">Gameplay</string>
<string name="settings_accessibility">Accessibility</string>
<string name="settings_large_areas">Use Large Areas</string>
<string name="rating">Feedback</string>
<string name="rating_message">If you like this game, please give us a feedback. It will help us a lot.</string>
<string name="used_software_text">This game uses the following third parties software:</string>
<string name="translators_text">This game was translated by the following people:</string>
<string name="sign_in_failed">Failed to sign in. Please check your network connection and try again.</string>
<string name="settings_gameplay">Joc</string>
<string name="settings_accessibility">Accessibilitat</string>
<string name="settings_large_areas">Usa grans àrees</string>
<string name="rating">Opina</string>
<string name="rating_message">Si t\'agrada aquest joc, si us plau dona\'ns la teva opinió. Ens ajudarà molt.</string>
<string name="used_software_text">Aquest joc usa el següent programari de tercers:</string>
<string name="translators_text">Aquest joc ha estat traduït per aquestes persones:</string>
<string name="sign_in_failed">No s\'ha pogut connectar. Si us plau verifica la teva connexió de xarxa i intenta-ho de nou.</string>
<string name="quit_confirm">You\'ll lose all moves on current game.\nBut you can also install the game before quit.</string>
<string name="you_won">You won!</string>
<string name="victories">Victories</string>
<string name="you_lost">You lost!</string>
<string name="defeats">Defeats</string>
<string name="game_over_desc_1">Good luck on your next game.</string>
<string name="you_won">Has guanyat!</string>
<string name="victories">Victòries</string>
<string name="you_lost">Has perdut!</string>
<string name="defeats">Derrotes</string>
<string name="game_over_desc_1">Que tinguis sort en el proper joc.</string>
<string name="game_over_desc_4">You finished the minefield in %1$d seconds.</string>
<string name="fail_to_share">Failed to share</string>
<string name="version_s">Version %1$s</string>
<string name="sound_effects">Sound Effects</string>
<string name="quit">Quit</string>
<string name="are_you_sure">Are you sure?</string>
<string name="fail_to_share">No s\'ha pogut compartir</string>
<string name="version_s">Versió %1$s</string>
<string name="sound_effects">Efecte de so</string>
<string name="quit">Surt</string>
<string name="are_you_sure">Esteu segurs?</string>
<string name="enable_automatic_flags">Enable automatic placing of flags</string>
<string name="open_areas">Open Areas</string>
<string name="total_time">Total Time</string>
<string name="total_time">Temps total</string>
<string name="average_time">Average Time</string>
<string name="performance">Performance</string>
<string name="performance">Rendiment</string>
<string name="ok">OK</string>
<string name="use_question_mark">Use Question Mark</string>
<string name="control">Controls</string>
<string name="arrow"></string>
<string name="flag_first">Flag First</string>
<string name="single_click">Single Click</string>
<string name="double_click">Double Click</string>
<string name="long_press">Long Press</string>
<string name="double_click">Doble clic</string>
<string name="long_press">Pulsació llarga</string>
<string name="open_tile">Open Tile</string>
<string name="flag_tile">Flag Tile</string>
<string name="retry">Retry</string>
<string name="retry">Reintenta</string>
<string name="empty">Empty</string>
<string name="unknown_error">Unknown error.</string>
<string name="leaderboards">Leaderboards</string>
<string name="unknown_error">Error desconegut.</string>
<string name="leaderboards">Llista de líders</string>
<string name="cancel">Cancel</string>
<string name="resume">Resume</string>
<string name="yes">Yes</string>
@ -91,9 +91,9 @@
<string name="desc_marked_area">Marked area</string>
<string name="desc_question_area">Doubtful area</string>
<string name="desc_wrongly_marked_area">Wrongly marked area</string>
<string name="exploded_mine">Exploded Mine</string>
<string name="game_started">Game Started</string>
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
<string name="exploded_mine">Mina explotada</string>
<string name="game_started">La partida ha començat</string>
<string name="you_exploded_a_mine">Has explotat una mina!</string>
<string name="flag_placed">Bandera col·locada!</string>
<string name="flag_removed">Has tret la bandera!</string>
</resources>

View file

@ -42,12 +42,12 @@
<string name="defeats">Défaites</string>
<string name="game_over_desc_1">Bonne chance pour votre prochaine partie.</string>
<string name="game_over_desc_4">Vous avez déminé le champ de mine en %1$d secondes.</string>
<string name="fail_to_share">Le partage a échoué</string>
<string name="fail_to_share">Échec du partage</string>
<string name="version_s">Version %1$s</string>
<string name="sound_effects">Effets sonores</string>
<string name="quit">Quitter</string>
<string name="are_you_sure">Vraiment ?</string>
<string name="enable_automatic_flags">Activer le placement automatique des drapeaux</string>
<string name="enable_automatic_flags">Activer le placement automatique de drapeaux</string>
<string name="open_areas">Zones ouvertes</string>
<string name="total_time">Temps de jeu total</string>
<string name="average_time">Temps de jeu moyen</string>
@ -57,9 +57,9 @@
<string name="control">Contrôles</string>
<string name="arrow"></string>
<string name="flag_first">Drapeau rapide</string>
<string name="single_click">Simple clic</string>
<string name="single_click">Clic simple</string>
<string name="double_click">Double clic</string>
<string name="long_press">Appui Long</string>
<string name="long_press">Appui prolongé</string>
<string name="open_tile">Ouvrir la tuile</string>
<string name="flag_tile">Placer le drapeau</string>
<string name="retry">Réessayer</string>

View file

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:locale="id">
<string name="app_name">Antimine</string>
<string name="app_description">Kosongkan ranjau tersembunyi dari medan ranjau</string>
<string name="games">Permainan</string>
<string name="previous_games">Tantangan Sebelumnya</string>
<string name="minefield">Kesulitan</string>
<string name="standard">Standar</string>
<string name="beginner">Pemula</string>
<string name="intermediate">Menengah</string>
<string name="expert">Ahli</string>
<string name="open">Buka</string>
<string name="settings">Pengaturan</string>
<string name="animations">Animasi</string>
<string name="vibration">Umpan Balik Haptik</string>
<string name="about">Tentang</string>
<string name="events">Statistik</string>
<string name="custom">Kustom</string>
<string name="start">Mulai</string>
<string name="width">Lebar</string>
<string name="height">Height</string>
<string name="mines">Tambang</string>
<string name="retry_sure">Bila Anda memulai permainan baru, kemajuan Anda kini akan hilang.</string>
<string name="show_licenses">Tampilkan lisensi</string>
<string name="new_game_request">Apakah Anda ingin memulai permainan baru?</string>
<string name="mines_remaining">%d ranjau</string>
<string name="game_time">Waktu permainan</string>
<string name="mine">Ranjau</string>
<string name="settings_general">Umum</string>
<string name="settings_gameplay">Aspek permainan</string>
<string name="settings_accessibility">Aksesibilitas</string>
<string name="settings_large_areas">Use Large Areas</string>
<string name="rating">Umpan Balik</string>
<string name="rating_message">Jika Anda menyukai game ini, tolong beri kami umpan balik. Itu akan banyak membantu kita.</string>
<string name="used_software_text">This game uses the following third parties software:</string>
<string name="translators_text">This game was translated by the following people:</string>
<string name="sign_in_failed">Tidak dapat tersambung. Silakan periksa koneksi jaringan anda dan coba lagi.</string>
<string name="quit_confirm"> Kemajuan Anda kini akan hilang.</string>
<string name="you_won">Anda menang!</string>
<string name="victories">Kemenangan</string>
<string name="you_lost">Anda Kalah!</string>
<string name="defeats">Kalah</string>
<string name="game_over_desc_1">Semoga sukses di game berikutnya.</string>
<string name="game_over_desc_4">Anda menyelesaikan permainan dalam %1$d detik.</string>
<string name="fail_to_share">Gagal berbagi</string>
<string name="version_s">Versi %1$s</string>
<string name="sound_effects">Efek suara</string>
<string name="quit">Keluar</string>
<string name="are_you_sure">Anda yakin?</string>
<string name="enable_automatic_flags">Aktifkan penempatan bendera otomatis</string>
<string name="open_areas">Buka</string>
<string name="total_time">Total waktu</string>
<string name="average_time">Waktu rata-rata</string>
<string name="performance">Kinerja</string>
<string name="ok">OK</string>
<string name="use_question_mark">Pakai Bendera Tanda Tanya</string>
<string name="control">Kontrol</string>
<string name="arrow"></string>
<string name="flag_first">Bendera</string>
<string name="single_click">Satu klik</string>
<string name="double_click">Klik dua kali</string>
<string name="long_press">Tekan Lama</string>
<string name="open_tile">Buka</string>
<string name="flag_tile">Bendera</string>
<string name="retry">Ulangi</string>
<string name="empty">Kosong</string>
<string name="unknown_error">Galat tak diketahui.</string>
<string name="leaderboards">Papan Peringkat</string>
<string name="cancel">Batal</string>
<string name="resume">Lanjutkan</string>
<string name="yes">Ya</string>
<string name="rating_button_no">Tidak</string>
<string name="general">Umum</string>
<string name="source_code">Kode sumber</string>
<string name="translation">Alih Bahasa</string>
<string name="licenses">Licenses</string>
<string name="google_play_games">Google Play Games</string>
<string name="install">Pasang</string>
<string name="connect">Menyambungkan</string>
<string name="connecting">Menghubungkan…</string>
<string name="disconnect">Terputus</string>
<string name="disconnected">Memutuskan</string>
<string name="new_game">Permainan Baru</string>
<string name="share">Bagikan</string>
<string name="share_menu">Bagikan…</string>
<string name="no_network">Tidak ada sambungan Internet.</string>
<string name="open_menu">Buka menu</string>
<string name="close_menu">Tutup Menu</string>
<string name="all_mines_disabled">All mines were disabled.</string>
<string name="desc_convered_area">Covered area</string>
<string name="desc_marked_area">Marked area</string>
<string name="desc_question_area">Doubtful area</string>
<string name="desc_wrongly_marked_area">Wrongly marked area</string>
<string name="exploded_mine">Exploded Mine</string>
<string name="game_started">Game Started</string>
<string name="you_exploded_a_mine">You exploded a mine!</string>
<string name="flag_placed">Flag placed!</string>
<string name="flag_removed">Flag removed!</string>
</resources>

View file

@ -52,16 +52,16 @@
<string name="total_time">总用时</string>
<string name="average_time">平均用时</string>
<string name="performance">性能</string>
<string name="ok">好的</string>
<string name="use_question_mark">使用问号</string>
<string name="ok">确定</string>
<string name="use_question_mark">使用问号标</string>
<string name="control">控制</string>
<string name="arrow"></string>
<string name="flag_first">标记在前</string>
<string name="single_click">次点</string>
<string name="single_click">单击</string>
<string name="double_click">双击</string>
<string name="long_press">长按</string>
<string name="open_tile">打开</string>
<string name="flag_tile"></string>
<string name="open_tile">打开图块</string>
<string name="flag_tile">帜图块</string>
<string name="retry">重试</string>
<string name="empty"></string>
<string name="unknown_error">未知错误。</string>

View file

@ -8,8 +8,8 @@
app:iconSpaceReserved="false">
<SwitchPreferenceCompat
android:checked="true"
android:defaultValue="true"
android:checked="false"
android:defaultValue="false"
android:key="preference_use_question_mark"
android:title="@string/use_question_mark"
app:iconSpaceReserved="false" />
@ -35,8 +35,8 @@
app:iconSpaceReserved="false"/>
<SwitchPreferenceCompat
android:checked="true"
android:defaultValue="true"
android:checked="false"
android:defaultValue="false"
android:key="preference_sound"
android:title="@string/sound_effects"
app:iconSpaceReserved="false" />

View file

@ -0,0 +1,13 @@
package dev.lucasnlm.antimine.common.level.di
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager
class DisabledHapticFeedbackManager : IHapticFeedbackManager {
override fun longPressFeedback() {
// Empty
}
override fun explosionFeedback() {
// Empty
}
}

View file

@ -14,8 +14,7 @@ import dev.lucasnlm.antimine.common.level.repository.IStatsRepository
import dev.lucasnlm.antimine.common.level.repository.MemorySavesRepository
import dev.lucasnlm.antimine.common.level.repository.MemoryStatsRepository
import dev.lucasnlm.antimine.common.level.utils.Clock
import dev.lucasnlm.antimine.common.level.utils.DisabledIHapticFeedbackInteractor
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackInteractor
import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager
@Module
@InstallIn(ApplicationComponent::class)
@ -39,5 +38,5 @@ class TestLevelModule {
fun provideStatsRepository(): IStatsRepository = MemoryStatsRepository()
@Provides
fun provideHapticFeedbackInteractor(): IHapticFeedbackInteractor = DisabledIHapticFeedbackInteractor()
fun provideHapticFeedbackInteractor(): IHapticFeedbackManager = DisabledHapticFeedbackManager()
}

View file

@ -6,8 +6,8 @@ android {
compileSdkVersion 30
defaultConfig {
versionCode 702031 // MMmmPPv
versionName '7.2.3'
versionCode 702041 // MMmmPPv
versionName '7.2.4'
minSdkVersion 16
targetSdkVersion 30
}

View file

@ -6,8 +6,8 @@ android {
compileSdkVersion 30
defaultConfig {
versionCode 702031 // MMmmPPv
versionName '7.2.3'
versionCode 702041 // MMmmPPv
versionName '7.2.4'
minSdkVersion 16
targetSdkVersion 30
}

View file

@ -9,8 +9,8 @@ android {
defaultConfig {
// versionCode and versionName must be hardcoded to support F-droid
versionCode 702031
versionName '7.2.3'
versionCode 702041
versionName '7.2.4'
applicationId 'dev.lucasnlm.antimine'
minSdkVersion 23
targetSdkVersion 30

View file

@ -176,7 +176,7 @@ class WatchGameActivity : AppCompatActivity(), AmbientModeSupport.AmbientCallbac
viewModel.stopClock()
GlobalScope.launch(context = Dispatchers.Main) {
viewModel.gameOver()
viewModel.gameOver(false)
messageText.text = getString(R.string.game_over)
waitAndShowNewGameButton()
}