diff --git a/app/build.gradle b/app/build.gradle index fa371747..6509eeb7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { defaultConfig { // versionCode and versionName must be hardcoded to support F-droid - versionCode 800111 - versionName '8.0.11' + versionCode 800101 + versionName '8.1.0' minSdkVersion 21 targetSdkVersion 30 multiDexEnabled true @@ -126,7 +126,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.0.1' // Google - implementation 'com.google.android.material:material:1.2.0' + implementation 'com.google.android.material:material:1.2.1' // Koin implementation 'org.koin:koin-android:2.1.6' diff --git a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt index e9db8a06..298176b2 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/GameActivity.kt @@ -2,11 +2,13 @@ package dev.lucasnlm.antimine import android.content.DialogInterface import android.content.Intent +import android.net.Uri import android.os.Bundle import android.text.format.DateUtils import android.view.View import android.view.WindowManager import android.widget.FrameLayout +import android.widget.Toast import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.TooltipCompat @@ -39,13 +41,14 @@ import dev.lucasnlm.antimine.share.ShareManager import dev.lucasnlm.antimine.stats.StatsActivity import dev.lucasnlm.antimine.support.SupportAppDialogFragment import dev.lucasnlm.antimine.theme.ThemeActivity +import dev.lucasnlm.antimine.tutorial.view.TutorialCompleteDialogFragment +import dev.lucasnlm.antimine.tutorial.view.TutorialLevelFragment import dev.lucasnlm.external.IBillingManager import dev.lucasnlm.external.IInstantAppManager import dev.lucasnlm.external.IPlayGamesManager import dev.lucasnlm.external.ReviewWrapper import dev.lucasnlm.external.model.PurchaseInfo import kotlinx.android.synthetic.main.activity_game.* -import kotlinx.android.synthetic.main.activity_game.ad_placeholder import kotlinx.android.synthetic.main.activity_game.minesCount import kotlinx.android.synthetic.main.activity_game.timer import kotlinx.android.synthetic.main.activity_tv_game.* @@ -101,7 +104,11 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O findViewById(R.id.levelContainer).doOnLayout { if (!isFinishing) { - loadGameFragment() + if (!preferencesRepository.isTutorialCompleted()) { + loadGameTutorial() + } else { + loadGameFragment() + } } } @@ -181,6 +188,13 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O currentSaveId = it } ) + + tips.observe( + this@GameActivity, + { + tipsCounter.text = it.toString() + } + ) } override fun onBackPressed() { @@ -241,8 +255,45 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O ) } - private fun refreshShortcutIcon(isEndGame: Boolean = false) { - if (isEndGame || instantAppManager.isEnabled(this)) { + private fun disableShortcutIcon(hide: Boolean = false) { + tipsCounter.visibility = View.GONE + shortcutIcon.apply { + visibility = if (hide) View.GONE else View.VISIBLE + isClickable = false + animate().alpha(0.3f).start() + } + } + + private fun refreshTipShortcutIcon() { + tipsCounter.apply { + visibility = View.VISIBLE + text = gameViewModel.getTips().toString() + } + + shortcutIcon.apply { + TooltipCompat.setTooltipText(this, getString(R.string.help)) + setImageResource(R.drawable.tip) + setColorFilter(minesCount.currentTextColor) + visibility = View.VISIBLE + animate().alpha(1.0f).start() + setOnClickListener { + lifecycleScope.launch { + analyticsManager.sentEvent(Analytics.UseTip) + + if (gameViewModel.getTips() > 0) { + if (!gameViewModel.revealRandomMine()) { + Toast.makeText(applicationContext, R.string.cant_do_it_now, Toast.LENGTH_SHORT).show() + } + } else { + Toast.makeText(applicationContext, R.string.help_win_a_game, Toast.LENGTH_SHORT).show() + } + } + } + } + } + + private fun refreshEndGameShortcutIcon(victory: Boolean = false, isOldGame: Boolean) { + if ((isOldGame || !victory) && !instantAppManager.isEnabled(this)) { shortcutIcon.apply { TooltipCompat.setTooltipText(this, getString(R.string.new_game)) setImageResource(R.drawable.retry) @@ -277,6 +328,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O } } + tipsCounter.visibility = View.GONE shortcutIcon.apply { when (status) { is Status.Over, is Status.Running -> { @@ -317,7 +369,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O } override fun onDrawerClosed(drawerView: View) { - if (hasNoOtherFocusedDialog()) { + if (hasNoOtherFocusedDialog() && hasActiveGameFragment()) { gameViewModel.resumeGame() } @@ -330,7 +382,9 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O } ) - if (preferencesRepository.isFirstUse()) { + if (preferencesRepository.isFirstUse() && + (preferencesRepository.isTutorialCompleted()) + ) { openDrawer(GravityCompat.START) preferencesRepository.completeFirstUse() } @@ -356,7 +410,9 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O R.id.previous_games -> openSaveHistory() R.id.stats -> openStats() R.id.play_games -> googlePlay() + R.id.translation -> openCrowdIn() R.id.remove_ads -> showSupportAppDialog() + R.id.tutorial -> loadGameTutorial() else -> handled = false } @@ -377,6 +433,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O // New Features findItem(R.id.themes).setActionView(R.layout.new_icon) + findItem(R.id.tutorial).setActionView(R.layout.new_icon) } } @@ -423,8 +480,27 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O private fun loadGameFragment() { supportFragmentManager.apply { - popBackStack() + findFragmentByTag(TutorialLevelFragment.TAG)?.let { it -> + beginTransaction().apply { + remove(it) + commitAllowingStateLoss() + } + } + if (findFragmentByTag(LevelFragment.TAG) == null) { + beginTransaction().apply { + replace(R.id.levelContainer, LevelFragment(), LevelFragment.TAG) + setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + commitAllowingStateLoss() + } + } + } + } + + private fun loadGameTutorial() { + disableShortcutIcon(true) + + supportFragmentManager.apply { findFragmentById(R.id.levelContainer)?.let { it -> beginTransaction().apply { remove(it) @@ -433,8 +509,8 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O } beginTransaction().apply { - replace(R.id.levelContainer, LevelFragment()) - setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + replace(R.id.levelContainer, TutorialLevelFragment(), TutorialLevelFragment.TAG) + setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) commitAllowingStateLoss() } } @@ -503,6 +579,12 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O } } + private fun showCompletedTutorialDialog() { + TutorialCompleteDialogFragment().run { + showAllowingStateLoss(supportFragmentManager, TutorialCompleteDialogFragment.TAG) + } + } + private fun showEndGameDialog(victory: Boolean) { val currentGameStatus = status if (currentGameStatus is Status.Over && !isFinishing && !drawer.isDrawerOpen(GravityCompat.START)) { @@ -514,7 +596,8 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O victory, score?.rightMines ?: 0, score?.totalMines ?: 0, - currentGameStatus.time + currentGameStatus.time, + if (victory) 1 else 0 ).apply { showAllowingStateLoss(supportFragmentManager, EndGameDialogFragment.TAG) } @@ -534,13 +617,17 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O } private fun changeDifficulty(newDifficulty: Difficulty) { + preferencesRepository.completeTutorial() + if (status == Status.PreGame) { - GlobalScope.launch { + loadGameFragment() + lifecycleScope.launch { gameViewModel.startNewGame(newDifficulty) } } else { newGameConfirmation { - GlobalScope.launch { + loadGameFragment() + lifecycleScope.launch { gameViewModel.startNewGame(newDifficulty) } } @@ -551,19 +638,34 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O when (event) { Event.ResumeGame -> { status = Status.Running - refreshShortcutIcon() + refreshTipShortcutIcon() } Event.StartNewGame -> { status = Status.PreGame - refreshShortcutIcon() + disableShortcutIcon() refreshAds() } Event.Resume, Event.Running -> { status = Status.Running gameViewModel.runClock() - refreshShortcutIcon() + refreshTipShortcutIcon() keepScreenOn(true) } + Event.StartTutorial -> { + status = Status.PreGame + gameViewModel.stopClock() + disableShortcutIcon(true) + loadGameTutorial() + } + Event.FinishTutorial -> { + gameViewModel.startNewGame(Difficulty.Beginner) + disableShortcutIcon() + loadGameFragment() + status = Status.Over(0, Score(4, 4, 25)) + analyticsManager.sentEvent(Analytics.TutorialCompleted) + preferencesRepository.completeTutorial() + showCompletedTutorialDialog() + } Event.Victory -> { val isResuming = (status == Status.PreGame) val score = Score( @@ -573,12 +675,14 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O ) status = Status.Over(currentTime, score) gameViewModel.stopClock() - gameViewModel.revealAllEmptyAreas() + gameViewModel.showAllEmptyAreas() gameViewModel.victory() - refreshShortcutIcon(true) + refreshEndGameShortcutIcon(true, isResuming) keepScreenOn(false) if (!isResuming) { + gameViewModel.addNewTip() + waitAndShowEndGameDialog( victory = true, await = false @@ -593,7 +697,7 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O totalArea ) status = Status.Over(currentTime, score) - refreshShortcutIcon(true) + refreshEndGameShortcutIcon(false, isResuming) keepScreenOn(false) gameViewModel.stopClock() @@ -653,6 +757,10 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O } == 0 } + private fun hasActiveGameFragment(): Boolean { + return supportFragmentManager.findFragmentByTag(LevelFragment.TAG) != null + } + override fun onDismiss(dialog: DialogInterface?) { gameViewModel.run { refreshUserPreferences() @@ -708,6 +816,12 @@ class GameActivity : ThematicActivity(R.layout.activity_game), DialogInterface.O } } + private fun openCrowdIn() { + Intent(Intent.ACTION_VIEW, Uri.parse("https://crowdin.com/project/antimine-android")).let { + startActivity(it) + } + } + private fun showSupportAppDialog() { if (supportFragmentManager.findFragmentByTag(SupportAppDialogFragment.TAG) == null && !instantAppManager.isEnabled(this) diff --git a/app/src/main/java/dev/lucasnlm/antimine/MainApplication.kt b/app/src/main/java/dev/lucasnlm/antimine/MainApplication.kt index 8ad2dd13..49e27396 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/MainApplication.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/MainApplication.kt @@ -5,6 +5,7 @@ import dev.lucasnlm.antimine.common.level.di.LevelModule import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager import dev.lucasnlm.antimine.core.analytics.models.Analytics import dev.lucasnlm.antimine.core.di.CommonModule +import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository import dev.lucasnlm.antimine.di.AppModule import dev.lucasnlm.antimine.di.ViewModelModule import dev.lucasnlm.external.IAdsManager @@ -14,6 +15,7 @@ import org.koin.core.context.startKoin open class MainApplication : MultiDexApplication() { private val analyticsManager: IAnalyticsManager by inject() + private val preferencesRepository: IPreferencesRepository by inject() private val adsManager: IAdsManager by inject() @@ -29,6 +31,10 @@ open class MainApplication : MultiDexApplication() { sentEvent(Analytics.Open) } + if (BuildConfig.FLAVOR == "foss") { + preferencesRepository.setPremiumFeatures(true) + } + adsManager.start(applicationContext) } } diff --git a/app/src/main/java/dev/lucasnlm/antimine/about/viewmodel/AboutViewModel.kt b/app/src/main/java/dev/lucasnlm/antimine/about/viewmodel/AboutViewModel.kt index 6f1e27c9..6d0e206c 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/about/viewmodel/AboutViewModel.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/about/viewmodel/AboutViewModel.kt @@ -31,12 +31,13 @@ class AboutViewModel( "Hungarian" to sequenceOf("Hermann Márk"), "Italian" to sequenceOf("Mattia - MisterWeeMan", "Nicola Lorenzetti"), "Japanese" to sequenceOf("Ryota Hasegawa"), + "Korean" to sequenceOf("Forever_"), "Norwegian" to sequenceOf("Eule Hecking"), "Polish" to sequenceOf("Sebastian Jasiński", "Sebastian Skibiński"), "Portuguese (BR)" to sequenceOf("Lucas Lima"), "Russian" to sequenceOf("gaich@xda", "ask45t", "Ekaterina543"), "Spanish" to sequenceOf("Alfredo Jara", "Aldo Rodriguez", "Inail"), - "Turkish" to sequenceOf("creuzwagen", "Fatih Fırıncı"), + "Turkish" to sequenceOf("arda şahin", "creuzwagen", "Fatih Fırıncı"), "Ukrainian" to sequenceOf("Dmitry Shuba"), "Vietnamese" to sequenceOf("pnhpnh"), ).map { diff --git a/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt b/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt index 5a28b4d4..bada1f99 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/di/ViewModelModule.kt @@ -10,6 +10,7 @@ import dev.lucasnlm.antimine.playgames.viewmodel.PlayGamesViewModel import dev.lucasnlm.antimine.stats.viewmodel.StatsViewModel import dev.lucasnlm.antimine.text.viewmodel.TextViewModel import dev.lucasnlm.antimine.theme.viewmodel.ThemeViewModel +import dev.lucasnlm.antimine.tutorial.viewmodel.TutorialViewModel import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.module @@ -24,6 +25,9 @@ val ViewModelModule = module { viewModel { TextViewModel(get()) } viewModel { ThemeViewModel(get(), get(), get(), get()) } viewModel { - GameViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) + GameViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) + } + viewModel { + TutorialViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } } diff --git a/app/src/main/java/dev/lucasnlm/antimine/level/view/EndGameDialogFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/level/view/EndGameDialogFragment.kt index dbe95e58..460f4057 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/level/view/EndGameDialogFragment.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/level/view/EndGameDialogFragment.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.app.Dialog import android.os.Bundle import android.view.LayoutInflater +import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.appcompat.app.AlertDialog @@ -12,11 +13,8 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.lifecycleScope import dev.lucasnlm.antimine.R import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel -import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository import dev.lucasnlm.antimine.level.viewmodel.EndGameDialogEvent import dev.lucasnlm.antimine.level.viewmodel.EndGameDialogViewModel -import dev.lucasnlm.external.Ads -import dev.lucasnlm.external.IAdsManager import dev.lucasnlm.external.IInstantAppManager import kotlinx.coroutines.flow.collect import org.koin.android.ext.android.inject @@ -25,9 +23,6 @@ import org.koin.androidx.viewmodel.ext.android.viewModel class EndGameDialogFragment : AppCompatDialogFragment() { private val instantAppManager: IInstantAppManager by inject() - private val adsManager: IAdsManager by inject() - private val preferencesRepository: IPreferencesRepository by inject() - private val endGameViewModel by viewModel() private val gameViewModel by sharedViewModel() @@ -40,7 +35,8 @@ class EndGameDialogFragment : AppCompatDialogFragment() { isVictory = if (getInt(DIALOG_TOTAL_MINES, 0) > 0) getBoolean(DIALOG_IS_VICTORY) else null, time = getLong(DIALOG_TIME, 0L), rightMines = getInt(DIALOG_RIGHT_MINES, 0), - totalMines = getInt(DIALOG_TOTAL_MINES, 0) + totalMines = getInt(DIALOG_TOTAL_MINES, 0), + received = getInt(DIALOG_RECEIVED, -1) ) ) } @@ -72,6 +68,15 @@ class EndGameDialogFragment : AppCompatDialogFragment() { } } + findViewById(R.id.received_message).apply { + if (state.received > 0 && state.isVictory == true) { + visibility = View.VISIBLE + text = getString(R.string.you_have_received, state.received) + } else { + visibility = View.GONE + } + } + if (state.isVictory == true) { if (!instantAppManager.isEnabled(context)) { setNeutralButton(R.string.share) { _, _ -> @@ -81,12 +86,6 @@ class EndGameDialogFragment : AppCompatDialogFragment() { } else { setNeutralButton(R.string.retry) { _, _ -> gameViewModel.retryObserver.postValue(Unit) - - activity?.let { - if (!preferencesRepository.isPremiumEnabled()) { - adsManager.requestRewarded(it, Ads.RewardsAds) - } - } } } } @@ -101,20 +100,27 @@ class EndGameDialogFragment : AppCompatDialogFragment() { }.create() companion object { - 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) - } + fun newInstance( + victory: Boolean, + rightMines: Int, + totalMines: Int, + time: Long, + received: Int, + ) = EndGameDialogFragment().apply { + arguments = Bundle().apply { + putBoolean(DIALOG_IS_VICTORY, victory) + putInt(DIALOG_RIGHT_MINES, rightMines) + putInt(DIALOG_TOTAL_MINES, totalMines) + putInt(DIALOG_RECEIVED, received) + putLong(DIALOG_TIME, time) } + } const val DIALOG_IS_VICTORY = "dialog_state" 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_RECEIVED = "dialog_received" val TAG = EndGameDialogFragment::class.simpleName!! } diff --git a/app/src/main/java/dev/lucasnlm/antimine/level/view/LevelFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/level/view/LevelFragment.kt index 718c1c7b..9b07bc80 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/level/view/LevelFragment.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/level/view/LevelFragment.kt @@ -24,7 +24,6 @@ open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - recyclerGrid = view.findViewById(R.id.recyclerGrid) recyclerGrid.doOnLayout { lifecycleScope.launch { val loadGameUid = checkLoadGameDeepLink() @@ -35,7 +34,7 @@ open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) { loadGameUid != null -> gameViewModel.loadGame(loadGameUid) newGameDeepLink != null -> gameViewModel.startNewGame(newGameDeepLink) retryDeepLink != null -> gameViewModel.retryGame(retryDeepLink) - else -> gameViewModel.loadLastGame() + else -> gameViewModel.loadGame() } withContext(Dispatchers.Main) { @@ -58,9 +57,9 @@ open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) { levelSetup.observe( viewLifecycleOwner, - { - getView()?.let { view -> - setupRecyclerViewSize(view, it) + { minefield -> + getView()?.doOnLayout { view -> + setupRecyclerViewSize(view, minefield) } } ) @@ -76,7 +75,7 @@ open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) { Event.Resume, Event.ResumeGame, Event.StartNewGame -> areaAdapter.setClickEnabled(true) - null -> { } + else -> { } } } ) @@ -113,4 +112,8 @@ open class LevelFragment : CommonLevelFragment(R.layout.fragment_level) { null } } + + companion object { + val TAG = LevelFragment::class.simpleName + } } diff --git a/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogEvent.kt b/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogEvent.kt index f27fa0d8..78a17850 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogEvent.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogEvent.kt @@ -8,6 +8,7 @@ sealed class EndGameDialogEvent { val time: Long, val rightMines: Int, val totalMines: Int, + val received: Int, ) : EndGameDialogEvent() data class ChangeEmoji( diff --git a/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogState.kt b/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogState.kt index 4c14300e..63e873d6 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogState.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogState.kt @@ -7,4 +7,5 @@ data class EndGameDialogState( val title: String, val message: String, val isVictory: Boolean?, + val received: Int ) diff --git a/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogViewModel.kt b/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogViewModel.kt index 8306dcd8..7c2f0611 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogViewModel.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/level/viewmodel/EndGameDialogViewModel.kt @@ -70,6 +70,7 @@ class EndGameDialogViewModel( "", "", false, + 0 ) override suspend fun mapEventToState(event: EndGameDialogEvent) = flow { @@ -80,7 +81,8 @@ class EndGameDialogViewModel( titleEmoji = randomVictoryEmoji(), title = context.getString(R.string.you_won), message = messageTo(event.time, event.isVictory), - isVictory = true + isVictory = true, + received = event.received ) } false -> { @@ -88,7 +90,8 @@ class EndGameDialogViewModel( titleEmoji = randomGameOverEmoji(), title = context.getString(R.string.you_lost), message = messageTo(event.time, event.isVictory), - isVictory = false + isVictory = false, + received = event.received ) } null -> { @@ -96,7 +99,8 @@ class EndGameDialogViewModel( titleEmoji = randomNeutralEmoji(), title = context.getString(R.string.new_game), message = context.getString(R.string.new_game_request), - isVictory = false + isVictory = false, + received = event.received ) } } diff --git a/app/src/main/java/dev/lucasnlm/antimine/support/SupportAppDialogFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/support/SupportAppDialogFragment.kt index 660d2f36..1c541aed 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/support/SupportAppDialogFragment.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/support/SupportAppDialogFragment.kt @@ -38,7 +38,7 @@ class SupportAppDialogFragment : AppCompatDialogFragment() { return AlertDialog.Builder(requireContext()).apply { setView(R.layout.dialog_payments) - setNeutralButton(R.string.rating_button_no) { _, _ -> + setNeutralButton(R.string.no) { _, _ -> analyticsManager.sentEvent(Analytics.DenyIapDialog) } diff --git a/app/src/main/java/dev/lucasnlm/antimine/theme/view/ExampleField.kt b/app/src/main/java/dev/lucasnlm/antimine/theme/view/ExampleField.kt new file mode 100644 index 00000000..ad8020db --- /dev/null +++ b/app/src/main/java/dev/lucasnlm/antimine/theme/view/ExampleField.kt @@ -0,0 +1,93 @@ +package dev.lucasnlm.antimine.theme.view + +import dev.lucasnlm.antimine.common.level.models.Area +import dev.lucasnlm.antimine.common.level.models.Mark + +object ExampleField { + fun getField() = listOf( + Area( + 0, + 0, + 0, + 1, + hasMine = false, + mistake = false, + mark = Mark.None, + isCovered = false + ), + Area( + 1, + 1, + 0, + 0, + hasMine = true, + mistake = false, + mark = Mark.None, + isCovered = false + ), + Area( + 2, + 2, + 0, + 0, + hasMine = true, + mistake = false, + mark = Mark.None, + isCovered = true + ), + Area( + 3, + 0, + 1, + 2, + hasMine = false, + mistake = false, + mark = Mark.None, + isCovered = false + ), + Area( + 4, + 1, + 1, + 3, + hasMine = false, + mistake = false, + mark = Mark.None, + isCovered = false + ), + Area( + 5, + 2, + 1, + 3, + hasMine = true, + mistake = false, + mark = Mark.Flag, + isCovered = true + ), + Area( + 6, + 0, + 2, + 0, + hasMine = true, + mistake = false, + mark = Mark.Question, + isCovered = true + ), + Area( + 7, + 1, + 2, + 4, + hasMine = false, + mistake = false, + mark = Mark.None, + isCovered = false + ), + Area( + 8, 2, 2, 0, + hasMine = false, mistake = false, mark = Mark.None, isCovered = true, revealed = true + ), + ) +} diff --git a/app/src/main/java/dev/lucasnlm/antimine/theme/view/ThemeAdapter.kt b/app/src/main/java/dev/lucasnlm/antimine/theme/view/ThemeAdapter.kt index bbea6df4..5990a146 100644 --- a/app/src/main/java/dev/lucasnlm/antimine/theme/view/ThemeAdapter.kt +++ b/app/src/main/java/dev/lucasnlm/antimine/theme/view/ThemeAdapter.kt @@ -12,7 +12,6 @@ import androidx.recyclerview.widget.RecyclerView import dev.lucasnlm.antimine.R 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.view.AreaView import dev.lucasnlm.antimine.core.themes.model.AppTheme import dev.lucasnlm.antimine.theme.viewmodel.ThemeEvent @@ -25,18 +24,7 @@ class ThemeAdapter( ) : RecyclerView.Adapter() { private val themes: List = themeViewModel.singleState().themes - - private val minefield = listOf( - Area(0, 0, 0, 1, hasMine = false, mistake = false, mark = Mark.None, isCovered = false), - Area(1, 1, 0, 0, hasMine = true, mistake = false, mark = Mark.None, isCovered = false), - Area(2, 2, 0, 0, hasMine = true, mistake = false, mark = Mark.None, isCovered = true), - Area(3, 0, 1, 2, hasMine = false, mistake = false, mark = Mark.None, isCovered = false), - Area(4, 1, 1, 3, hasMine = false, mistake = false, mark = Mark.None, isCovered = false), - Area(5, 2, 1, 3, hasMine = true, mistake = false, mark = Mark.Flag, isCovered = true), - Area(6, 0, 2, 0, hasMine = true, mistake = false, mark = Mark.Question, isCovered = true), - Area(7, 1, 2, 4, hasMine = false, mistake = false, mark = Mark.None, isCovered = false), - Area(8, 2, 2, 0, hasMine = false, mistake = false, mark = Mark.None, isCovered = true), - ) + private val minefield = ExampleField.getField() init { setHasStableIds(true) diff --git a/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialAreaAdapter.kt b/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialAreaAdapter.kt new file mode 100644 index 00000000..afc2e99a --- /dev/null +++ b/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialAreaAdapter.kt @@ -0,0 +1,171 @@ +package dev.lucasnlm.antimine.tutorial.view + +import android.annotation.SuppressLint +import android.content.Context +import android.util.Log +import android.view.GestureDetector +import android.view.KeyEvent +import android.view.MotionEvent +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import dev.lucasnlm.antimine.common.level.models.Area +import dev.lucasnlm.antimine.common.level.models.AreaPaintSettings +import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository +import dev.lucasnlm.antimine.common.level.view.AreaAdapter +import dev.lucasnlm.antimine.common.level.view.AreaView +import dev.lucasnlm.antimine.common.level.view.AreaViewHolder +import dev.lucasnlm.antimine.core.control.ControlStyle +import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository +import dev.lucasnlm.antimine.tutorial.viewmodel.TutorialViewModel +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +class TutorialAreaAdapter( + context: Context, + private val viewModel: TutorialViewModel, + private val preferencesRepository: IPreferencesRepository, + dimensionRepository: IDimensionRepository, +) : RecyclerView.Adapter() { + + private var field = listOf() + private var isLowBitAmbient = false + private var isAmbientMode = false + private val paintSettings: AreaPaintSettings + + private var clickEnabled: Boolean = false + + init { + setHasStableIds(false) + paintSettings = AreaAdapter.createAreaPaintSettings(context.applicationContext, dimensionRepository.areaSize()) + } + + fun setAmbientMode(isAmbientMode: Boolean, isLowBitAmbient: Boolean) { + this.isLowBitAmbient = isLowBitAmbient + this.isAmbientMode = isAmbientMode + } + + fun setClickEnabled(value: Boolean) { + clickEnabled = value + } + + fun bindField(field: List) { + this.field = field + notifyDataSetChanged() + } + + override fun getItemCount(): Int = field.size + + private fun AreaView.onClickablePosition(position: Int, action: suspend (Int) -> Unit): Boolean { + return when { + position == RecyclerView.NO_POSITION -> { + Log.d(TAG, "Item no longer exists.") + false + } + clickEnabled -> { + requestFocus() + GlobalScope.launch { + action(position) + } + true + } + else -> false + } + } + + @SuppressLint("ClickableViewAccessibility") + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AreaViewHolder { + val view = AreaView(parent.context) + return AreaViewHolder(view).apply { + val style = preferencesRepository.controlStyle() + if (style == ControlStyle.DoubleClick || style == ControlStyle.DoubleClickInverted) { + view.isClickable = true + view.setOnDoubleClickListener( + object : GestureDetector.OnDoubleTapListener { + override fun onDoubleTap(e: MotionEvent?): Boolean { + return view.onClickablePosition(absoluteAdapterPosition) { + viewModel.onDoubleClick(it) + } + } + + override fun onDoubleTapEvent(e: MotionEvent?): Boolean { + return false + } + + override fun onSingleTapConfirmed(e: MotionEvent?): Boolean { + return view.onClickablePosition(absoluteAdapterPosition) { + viewModel.onSingleClick(it) + } + } + } + ) + } else { + view.setOnTouchListener { _, motionEvent -> + when (motionEvent.action) { + MotionEvent.ACTION_DOWN -> { + view.isPressed = true + true + } + MotionEvent.ACTION_UP -> { + view.isPressed = false + val dt = motionEvent.eventTime - motionEvent.downTime + + if (dt > preferencesRepository.customLongPressTimeout()) { + view.onClickablePosition(absoluteAdapterPosition) { + viewModel.onLongClick(it) + } + } else { + view.onClickablePosition(absoluteAdapterPosition) { + viewModel.onSingleClick(it) + } + } + } + else -> false + } + } + } + + view.setOnKeyListener { _, keyCode, keyEvent -> + var handled = false + if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { + when (keyEvent.action) { + KeyEvent.ACTION_DOWN -> { + handled = true + view.isPressed = true + } + KeyEvent.ACTION_UP -> { + handled = true + view.isPressed = false + val dt = keyEvent.eventTime - keyEvent.downTime + if (dt > preferencesRepository.customLongPressTimeout()) { + view.onClickablePosition(absoluteAdapterPosition) { + viewModel.onLongClick(it) + } + } else { + view.onClickablePosition(absoluteAdapterPosition) { + viewModel.onSingleClick(it) + } + } + } + } + } + handled + } + } + } + + private fun getItem(position: Int) = field[position] + + override fun getItemId(position: Int): Long = getItem(position).id.toLong() + + override fun onBindViewHolder(holder: AreaViewHolder, position: Int) { + val field = getItem(position) + holder.run { + val areaView = itemView as AreaView + areaView.bindField(field, viewModel.getAppTheme(), isAmbientMode, isLowBitAmbient, paintSettings) + } + } + + companion object { + val TAG = TutorialAreaAdapter::class.simpleName!! + } +} diff --git a/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialCompleteDialogFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialCompleteDialogFragment.kt new file mode 100644 index 00000000..c99ad4b0 --- /dev/null +++ b/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialCompleteDialogFragment.kt @@ -0,0 +1,57 @@ +package dev.lucasnlm.antimine.tutorial.view + +import android.annotation.SuppressLint +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatDialogFragment +import androidx.fragment.app.FragmentManager +import dev.lucasnlm.antimine.R +import dev.lucasnlm.antimine.common.level.models.Difficulty +import dev.lucasnlm.antimine.common.level.models.Event +import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel +import org.koin.androidx.viewmodel.ext.android.sharedViewModel + +class TutorialCompleteDialogFragment : AppCompatDialogFragment() { + private val gameViewModel by sharedViewModel() + + fun showAllowingStateLoss(manager: FragmentManager, tag: String?) { + val fragmentTransaction = manager.beginTransaction() + fragmentTransaction.add(this, tag) + fragmentTransaction.commitAllowingStateLoss() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + gameViewModel.startNewGame(Difficulty.Beginner) + } + + @SuppressLint("InflateParams") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = + AlertDialog.Builder(requireContext()).apply { + val view = LayoutInflater + .from(context) + .inflate(R.layout.dialog_end_game, null, false) + .apply { + findViewById(R.id.title).text = getString(R.string.tutorial_completed) + findViewById(R.id.subtitle).visibility = View.GONE + findViewById(R.id.received_message).visibility = View.GONE + findViewById(R.id.title_emoji) + .setImageResource(R.drawable.emoji_beaming_face_with_smiling_eyes) + } + + setView(view) + setNeutralButton(R.string.retry) { _, _ -> + gameViewModel.eventObserver.postValue(Event.StartTutorial) + } + setPositiveButton(R.string.resume) { _, _ -> } + }.create() + + companion object { + val TAG = TutorialCompleteDialogFragment::class.simpleName!! + } +} diff --git a/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialField.kt b/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialField.kt new file mode 100644 index 00000000..af34d443 --- /dev/null +++ b/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialField.kt @@ -0,0 +1,254 @@ +package dev.lucasnlm.antimine.tutorial.view + +import dev.lucasnlm.antimine.common.level.models.Area +import dev.lucasnlm.antimine.common.level.models.Mark + +object TutorialField { + fun getStep0(): List { + return (0..24).map { + Area(it, 0, 0, isCovered = true) + }.toList() + } + + private fun getDefaultStep(): MutableList { + return mutableListOf( + Area( + 0, 0, 0, 0, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 1, 1, 0, 0, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 2, 2, 0, 0, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 3, 3, 0, 2, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 4, 4, 0, 0, hasMine = true, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 5, 0, 1, 1, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 6, 1, 1, 1, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 7, 2, 1, 0, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 8, 3, 1, 2, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 9, 4, 1, 0, hasMine = true, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 10, 0, 2, 0, hasMine = true, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 11, 1, 2, 1, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 12, 2, 2, 0, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 13, 3, 2, 1, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 14, 4, 2, 1, hasMine = false, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 15, 0, 3, 1, hasMine = false, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 16, 1, 3, 2, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 17, 2, 3, 1, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 18, 3, 3, 1, hasMine = false, + mistake = false, isCovered = false, mark = Mark.None, + highlighted = false, + ), + Area( + 19, 4, 3, 0, hasMine = false, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 20, 0, 4, 0, hasMine = false, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 21, 1, 4, 1, hasMine = false, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 22, 2, 4, 0, hasMine = true, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 23, 3, 4, 1, hasMine = false, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + Area( + 24, 4, 4, 0, hasMine = false, + mistake = false, isCovered = true, mark = Mark.None, + highlighted = false, + ), + ) + } + + fun getStep1(): List { + return getDefaultStep().apply { + this[6] = this[6].copy(highlighted = true) + this[10] = this[10].copy(highlighted = true) + } + } + + fun getStep2(): List { + return getDefaultStep().apply { + this[10] = this[10].copy(mark = Mark.Flag) + this[11] = this[11].copy(highlighted = true) + this[15] = this[15].copy(highlighted = true) + } + } + + fun getStep3(): List { + return getDefaultStep().apply { + this[10] = this[10].copy(mark = Mark.Flag) + this[15] = this[15].copy(highlighted = true, isCovered = false) + this[20] = this[20].copy(highlighted = true) + this[21] = this[21].copy(highlighted = true) + } + } + + fun getStep4(): List { + return getDefaultStep().apply { + this[10] = this[10].copy(mark = Mark.Flag) + this[15] = this[15].copy(isCovered = false) + this[20] = this[20].copy(isCovered = false) + this[21] = this[21].copy(isCovered = false) + this[16] = this[16].copy(highlighted = true) + this[22] = this[22].copy(highlighted = true) + } + } + + fun getStep5(): List { + return getDefaultStep().apply { + this[10] = this[10].copy(mark = Mark.Flag) + this[15] = this[15].copy(isCovered = false) + this[20] = this[20].copy(isCovered = false) + this[21] = this[21].copy(isCovered = false) + this[17] = this[17].copy(highlighted = true) + this[23] = this[23].copy(highlighted = true) + this[22] = this[22].copy(mark = Mark.Flag) + } + } + + fun getStep6(): List { + return getDefaultStep().apply { + this[10] = this[10].copy(mark = Mark.Flag) + this[15] = this[15].copy(isCovered = false) + this[20] = this[20].copy(isCovered = false) + this[21] = this[21].copy(isCovered = false) + this[23] = this[23].copy(isCovered = false) + this[22] = this[22].copy(mark = Mark.Flag) + this[18] = this[18].copy(highlighted = true) + + this[24] = this[24].copy(highlighted = true) + this[19] = this[19].copy(highlighted = true) + this[14] = this[14].copy(highlighted = true) + } + } + + fun getStep7(): List { + return getDefaultStep().apply { + this[10] = this[10].copy(mark = Mark.Flag) + this[15] = this[15].copy(isCovered = false) + this[20] = this[20].copy(isCovered = false) + this[21] = this[21].copy(isCovered = false) + this[23] = this[23].copy(isCovered = false) + this[22] = this[22].copy(mark = Mark.Flag) + this[24] = this[24].copy(isCovered = false) + this[19] = this[19].copy(isCovered = false) + this[14] = this[14].copy(isCovered = false) + this[13] = this[13].copy(highlighted = true) + this[9] = this[9].copy(highlighted = true) + } + } + + fun getStep8(): List { + return getDefaultStep().apply { + this[10] = this[10].copy(mark = Mark.Flag) + this[15] = this[15].copy(isCovered = false) + this[20] = this[20].copy(isCovered = false) + this[21] = this[21].copy(isCovered = false) + this[23] = this[23].copy(isCovered = false) + this[22] = this[22].copy(mark = Mark.Flag) + this[24] = this[24].copy(isCovered = false) + this[19] = this[19].copy(isCovered = false) + this[14] = this[14].copy(isCovered = false) + this[8] = this[8].copy(highlighted = true) + this[4] = this[4].copy(highlighted = true) + this[9] = this[9].copy(mark = Mark.Flag) + } + } + + fun getStep9(): List { + return getDefaultStep().apply { + this[10] = this[10].copy(mark = Mark.Flag) + this[15] = this[15].copy(isCovered = false) + this[20] = this[20].copy(isCovered = false) + this[21] = this[21].copy(isCovered = false) + this[23] = this[23].copy(isCovered = false) + this[22] = this[22].copy(mark = Mark.Flag) + this[24] = this[24].copy(isCovered = false) + this[19] = this[19].copy(isCovered = false) + this[14] = this[14].copy(isCovered = false) + this[4] = this[4].copy(mark = Mark.Flag) + this[9] = this[9].copy(mark = Mark.Flag) + } + } +} diff --git a/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialLevelFragment.kt b/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialLevelFragment.kt new file mode 100644 index 00000000..3bf764be --- /dev/null +++ b/app/src/main/java/dev/lucasnlm/antimine/tutorial/view/TutorialLevelFragment.kt @@ -0,0 +1,138 @@ +package dev.lucasnlm.antimine.tutorial.view + +import android.graphics.Color +import android.os.Bundle +import android.view.View +import androidx.core.content.ContextCompat +import androidx.core.text.bold +import androidx.core.text.buildSpannedString +import androidx.core.text.color +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView +import dev.lucasnlm.antimine.R +import dev.lucasnlm.antimine.common.level.models.Event +import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository +import dev.lucasnlm.antimine.common.level.view.SpaceItemDecoration +import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel +import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository +import dev.lucasnlm.antimine.tutorial.viewmodel.TutorialViewModel +import kotlinx.android.synthetic.main.fragment_tutorial_level.* +import kotlinx.coroutines.flow.collect +import org.koin.android.ext.android.inject +import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import org.koin.androidx.viewmodel.ext.android.viewModel + +class TutorialLevelFragment : Fragment(R.layout.fragment_tutorial_level) { + private val dimensionRepository: IDimensionRepository by inject() + private val preferencesRepository: IPreferencesRepository by inject() + private val tutorialViewModel by viewModel() + private val gameViewModel by sharedViewModel() + private val areaAdapter by lazy { + TutorialAreaAdapter(requireContext(), tutorialViewModel, preferencesRepository, dimensionRepository).apply { + setClickEnabled(true) + } + } + private lateinit var recyclerGrid: RecyclerView + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + recyclerGrid = view.findViewById(R.id.recyclerGrid) + + recyclerGrid.apply { + addItemDecoration(SpaceItemDecoration(R.dimen.field_padding)) + setHasFixedSize(true) + layoutManager = GridLayoutManager(view.context, 5) + adapter = areaAdapter + } + + lifecycleScope.launchWhenCreated { + tutorialViewModel.run { + field.observe( + viewLifecycleOwner, + { list -> + gameViewModel.stopClock() + gameViewModel.elapsedTimeSeconds.postValue(0L) + gameViewModel.mineCount.postValue( + list.count { it.hasMine }.coerceAtLeast(4) - list.count { it.mark.isFlag() } + ) + areaAdapter.bindField(list) + + if (tutorialState.value.completed) { + gameViewModel.eventObserver.postValue(Event.FinishTutorial) + } + } + ) + } + } + + lifecycleScope.launchWhenCreated { + tutorialViewModel.tutorialState.collect { + val flagAction = tutorialViewModel.flagActionLabel() + val openAction = tutorialViewModel.openActionLabel() + + tutorial_top.apply { + text = buildSpannedString { + it.topMessage + .splitKeeping(flagAction, openAction) + .forEach { + when (it) { + flagAction, openAction -> { + bold { color(ContextCompat.getColor(context, R.color.accent)) { append(it) } } + } + else -> append(it) + } + } + } + setTextColor( + Color.argb( + 255, + Color.red(tutorial_top.currentTextColor), + Color.green(tutorial_top.currentTextColor), + Color.blue(tutorial_top.currentTextColor), + ), + ) + } + tutorial_bottom.apply { + text = buildSpannedString { + it.bottomMessage + .splitKeeping(flagAction, openAction) + .forEach { + when (it) { + flagAction, openAction -> { + bold { color(ContextCompat.getColor(context, R.color.accent)) { append(it) } } + } + else -> append(it) + } + } + } + setTextColor( + Color.argb( + 255, + Color.red(tutorial_top.currentTextColor), + Color.green(tutorial_top.currentTextColor), + Color.blue(tutorial_top.currentTextColor), + ), + ) + } + } + } + } + + private fun String.splitKeeping(str: String): List { + return this.split(str).flatMap { listOf(it, str) }.dropLast(1).filterNot { it.isEmpty() } + } + + private fun String.splitKeeping(vararg targetStrings: String): List { + var res = listOf(this) + targetStrings.forEach { str -> + res = res.flatMap { it.splitKeeping(str) } + } + return res + } + + companion object { + val TAG = TutorialLevelFragment::class.simpleName + } +} diff --git a/app/src/main/java/dev/lucasnlm/antimine/tutorial/viewmodel/TutorialState.kt b/app/src/main/java/dev/lucasnlm/antimine/tutorial/viewmodel/TutorialState.kt new file mode 100644 index 00000000..614f0fa3 --- /dev/null +++ b/app/src/main/java/dev/lucasnlm/antimine/tutorial/viewmodel/TutorialState.kt @@ -0,0 +1,8 @@ +package dev.lucasnlm.antimine.tutorial.viewmodel + +data class TutorialState( + val step: Int, + val topMessage: String, + val bottomMessage: String, + val completed: Boolean, +) diff --git a/app/src/main/java/dev/lucasnlm/antimine/tutorial/viewmodel/TutorialViewModel.kt b/app/src/main/java/dev/lucasnlm/antimine/tutorial/viewmodel/TutorialViewModel.kt new file mode 100644 index 00000000..73ca8bef --- /dev/null +++ b/app/src/main/java/dev/lucasnlm/antimine/tutorial/viewmodel/TutorialViewModel.kt @@ -0,0 +1,220 @@ +package dev.lucasnlm.antimine.tutorial.viewmodel + +import android.content.Context +import dev.lucasnlm.antimine.common.R +import dev.lucasnlm.antimine.common.level.models.Area +import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository +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.repository.ITipRepository +import dev.lucasnlm.antimine.common.level.utils.Clock +import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager +import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel +import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager +import dev.lucasnlm.antimine.core.analytics.models.Analytics +import dev.lucasnlm.antimine.core.control.ControlStyle +import dev.lucasnlm.antimine.core.preferences.IPreferencesRepository +import dev.lucasnlm.antimine.core.sound.ISoundManager +import dev.lucasnlm.antimine.core.themes.repository.IThemeRepository +import dev.lucasnlm.antimine.tutorial.view.TutorialField +import dev.lucasnlm.external.IPlayGamesManager +import kotlinx.coroutines.flow.MutableStateFlow + +class TutorialViewModel( + savesRepository: ISavesRepository, + statsRepository: IStatsRepository, + dimensionRepository: IDimensionRepository, + themeRepository: IThemeRepository, + soundManager: ISoundManager, + minefieldRepository: IMinefieldRepository, + analyticsManager: IAnalyticsManager, + playGamesManager: IPlayGamesManager, + tipRepository: ITipRepository, + private val clock: Clock, + private val context: Context, + private val hapticFeedbackManager: IHapticFeedbackManager, + private val preferencesRepository: IPreferencesRepository, +) : GameViewModel( + savesRepository, + statsRepository, + dimensionRepository, + preferencesRepository, + hapticFeedbackManager, + themeRepository, + soundManager, + minefieldRepository, + analyticsManager, + playGamesManager, + tipRepository, + clock, +) { + val tutorialState = MutableStateFlow( + TutorialState( + 0, + context.getString(R.string.tutorial), + context.getString(R.string.tutorial_0_bottom, openActionLabel()), + completed = false, + ) + ) + + init { + field.postValue(TutorialField.getStep0()) + analyticsManager.sentEvent(Analytics.TutorialStarted) + } + + private fun currentStep(): Int { + return tutorialState.value.step + } + + fun openActionLabel(): String = + when (preferencesRepository.controlStyle()) { + ControlStyle.Standard -> context.getString(R.string.single_click) + ControlStyle.FastFlag -> context.getString(R.string.long_press) + ControlStyle.DoubleClick -> context.getString(R.string.double_click) + ControlStyle.DoubleClickInverted -> context.getString(R.string.single_click) + } + + fun flagActionLabel(): String = + when (preferencesRepository.controlStyle()) { + ControlStyle.Standard -> context.getString(R.string.long_press) + ControlStyle.FastFlag -> context.getString(R.string.single_click) + ControlStyle.DoubleClick -> context.getString(R.string.single_click) + ControlStyle.DoubleClickInverted -> context.getString(R.string.double_click) + } + + private fun postStep(step: List, top: String, bottom: String, completed: Boolean = false) { + field.postValue(step) + clock.stop() + tutorialState.value = tutorialState.value.copy( + completed = completed, + step = currentStep() + 1, + topMessage = top, + bottomMessage = bottom, + ) + } + + private fun openTileAction(index: Int) { + when (currentStep()) { + 0 -> { + postStep( + TutorialField.getStep1(), + context.getString(R.string.tutorial_1_top), + context.getString(R.string.tutorial_1_bottom, flagActionLabel()), + ) + } + 2 -> { + if (index == 15) { + postStep( + TutorialField.getStep3(), + context.getString(R.string.tutorial_3_top), + context.getString(R.string.tutorial_3_bottom), + ) + } + } + 3 -> { + if (index == 20 || index == 21) { + postStep( + TutorialField.getStep4(), + context.getString(R.string.tutorial_4_top), + context.getString(R.string.tutorial_4_bottom, flagActionLabel()), + ) + } + } + 5 -> { + if (index == 23) { + postStep( + TutorialField.getStep6(), + context.getString(R.string.tutorial_5_top), + context.getString(R.string.tutorial_5_bottom, openActionLabel(), flagActionLabel()), + ) + } + } + 6 -> { + if (index == 24 || index == 19 || index == 14) { + postStep( + TutorialField.getStep7(), + context.getString(R.string.tutorial_5_top), + context.getString(R.string.tutorial_5_bottom, openActionLabel(), flagActionLabel()), + ) + } + } + else -> { + hapticFeedbackManager.explosionFeedback() + } + } + } + + private fun longTileAction(index: Int) { + when (currentStep()) { + 1 -> { + if (index == 10) { + postStep( + TutorialField.getStep2(), + context.getString(R.string.tutorial_2_top), + context.getString(R.string.tutorial_2_bottom, openActionLabel()), + ) + } + } + 4 -> { + if (index == 22) { + postStep( + TutorialField.getStep5(), + context.getString(R.string.tutorial_5_top), + context.getString(R.string.tutorial_5_bottom, openActionLabel(), flagActionLabel()), + ) + } + } + 7 -> { + if (index == 9) { + postStep( + TutorialField.getStep8(), + context.getString(R.string.tutorial_5_top), + context.getString(R.string.tutorial_5_bottom, openActionLabel(), flagActionLabel()), + ) + } + } + 8 -> { + if (index == 4) { + postStep( + TutorialField.getStep9(), + context.getString(R.string.tutorial_5_top), + context.getString(R.string.tutorial_5_bottom, openActionLabel(), flagActionLabel()), + completed = true + ) + } + } + else -> { + hapticFeedbackManager.explosionFeedback() + } + } + } + + override suspend fun onDoubleClick(index: Int) { + clock.stop() + when (preferencesRepository.controlStyle()) { + ControlStyle.DoubleClick -> openTileAction(index) + ControlStyle.DoubleClickInverted -> longTileAction(index) + else -> {} + } + } + + override suspend fun onSingleClick(index: Int) { + clock.stop() + when (preferencesRepository.controlStyle()) { + ControlStyle.Standard -> openTileAction(index) + ControlStyle.FastFlag -> longTileAction(index) + ControlStyle.DoubleClick -> longTileAction(index) + ControlStyle.DoubleClickInverted -> openTileAction(index) + } + } + + override suspend fun onLongClick(index: Int) { + clock.stop() + when (preferencesRepository.controlStyle()) { + ControlStyle.Standard -> longTileAction(index) + ControlStyle.FastFlag -> openTileAction(index) + else -> {} + } + } +} diff --git a/app/src/main/res/drawable/tip.xml b/app/src/main/res/drawable/tip.xml new file mode 100644 index 00000000..275b216e --- /dev/null +++ b/app/src/main/res/drawable/tip.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/translate.xml b/app/src/main/res/drawable/translate.xml new file mode 100644 index 00000000..475bb6e2 --- /dev/null +++ b/app/src/main/res/drawable/translate.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/tutorial.xml b/app/src/main/res/drawable/tutorial.xml new file mode 100644 index 00000000..20d5e297 --- /dev/null +++ b/app/src/main/res/drawable/tutorial.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_game.xml b/app/src/main/res/layout/activity_game.xml index 8fb58b39..45067764 100644 --- a/app/src/main/res/layout/activity_game.xml +++ b/app/src/main/res/layout/activity_game.xml @@ -83,10 +83,26 @@ android:background="?selectableItemBackgroundBorderless" android:contentDescription="@string/new_game" android:padding="14dp" + android:alpha="0.0" + android:clickable="false" ads:layout_constraintRight_toRightOf="parent" ads:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/retry" - tools:visibility="visible"/> + tools:alpha="1.0"/> + + + + diff --git a/app/src/main/res/layout/fragment_tutorial_level.xml b/app/src/main/res/layout/fragment_tutorial_level.xml new file mode 100644 index 00000000..16d7cae2 --- /dev/null +++ b/app/src/main/res/layout/fragment_tutorial_level.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + diff --git a/app/src/main/res/menu/nav_menu.xml b/app/src/main/res/menu/nav_menu.xml index 80e85058..c8f440e0 100644 --- a/app/src/main/res/menu/nav_menu.xml +++ b/app/src/main/res/menu/nav_menu.xml @@ -78,6 +78,12 @@ android:checkable="false" android:icon="@drawable/settings" android:title="@string/settings" /> + + @@ -103,6 +109,12 @@ android:checkable="false" android:title="@string/rating" /> + + = 1 -> { + preferencesRepository.setTips(tips - 1) + true + } + preferencesRepository.getExtraTips() >= 1 -> { + preferencesRepository.setExtraTips(extra - 1) + true + } + else -> { + false + } + } + } + + override fun increaseTip() { + val newValue = (preferencesRepository.getTips() + 1).coerceAtMost(MAX_TIPS).coerceAtLeast(0) + preferencesRepository.setTips(newValue) + } + + override fun getTotalTips(): Int { + return preferencesRepository.getExtraTips() + preferencesRepository.getTips() + } + + companion object { + const val MAX_TIPS = 15 + } +} diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/view/AreaPainter.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/view/AreaPainter.kt index 1c582d7e..3aef0140 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/view/AreaPainter.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/view/AreaPainter.kt @@ -79,7 +79,21 @@ fun Area.paintOnCanvas( ) question?.draw(canvas) } - else -> {} + else -> { + if (revealed) { + val padding = minePadding ?: context.resources.getDimension(R.dimen.mine_padding).toInt() + + val revealedDrawable = ContextCompat.getDrawable(context, theme.assets.revealed) + + revealedDrawable?.setBounds( + rectF.left.toInt() + padding, + rectF.top.toInt() + padding, + rectF.right.toInt() - padding, + rectF.bottom.toInt() - padding + ) + revealedDrawable?.draw(canvas) + } + } } } else { if (isAmbientMode) { diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/view/CommonLevelFragment.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/view/CommonLevelFragment.kt index 58945846..a8574e0b 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/view/CommonLevelFragment.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/view/CommonLevelFragment.kt @@ -1,11 +1,12 @@ package dev.lucasnlm.antimine.common.level.view import android.content.Context -import android.text.format.DateUtils +import android.os.Bundle import android.view.View import androidx.annotation.LayoutRes import androidx.fragment.app.Fragment import androidx.recyclerview.widget.RecyclerView +import dev.lucasnlm.antimine.common.R import dev.lucasnlm.antimine.common.level.models.Minefield import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository import dev.lucasnlm.antimine.common.level.viewmodel.GameViewModel @@ -23,6 +24,11 @@ abstract class CommonLevelFragment(@LayoutRes val contentLayoutId: Int) : Fragme } protected lateinit var recyclerGrid: RecyclerView + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + recyclerGrid = view.findViewById(R.id.recyclerGrid) + } + private fun makeNewLayoutManager(boardWidth: Int) = FixedGridLayoutManager().apply { setTotalColumnCount(boardWidth) @@ -35,12 +41,6 @@ abstract class CommonLevelFragment(@LayoutRes val contentLayoutId: Int) : Fragme setPadding(horizontalPadding, verticalPadding, 0, 0) layoutManager = makeNewLayoutManager(levelSetup.width) adapter = areaAdapter - alpha = 0.0f - - animate().apply { - alpha(1.0f) - duration = DateUtils.SECOND_IN_MILLIS - }.start() } } diff --git a/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt b/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt index 54351f78..93d01104 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/common/level/viewmodel/GameViewModel.kt @@ -15,6 +15,7 @@ import dev.lucasnlm.antimine.common.level.repository.IDimensionRepository 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.repository.ITipRepository import dev.lucasnlm.antimine.common.level.utils.Clock import dev.lucasnlm.antimine.common.level.utils.IHapticFeedbackManager import dev.lucasnlm.antimine.core.analytics.IAnalyticsManager @@ -35,7 +36,7 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class GameViewModel( +open class GameViewModel( private val savesRepository: ISavesRepository, private val statsRepository: IStatsRepository, private val dimensionRepository: IDimensionRepository, @@ -46,6 +47,7 @@ class GameViewModel( private val minefieldRepository: IMinefieldRepository, private val analyticsManager: IAnalyticsManager, private val playGamesManager: IPlayGamesManager, + private val tipRepository: ITipRepository, private val clock: Clock, ) : ViewModel() { val eventObserver = MutableLiveData() @@ -62,6 +64,7 @@ class GameViewModel( val difficulty = MutableLiveData() val levelSetup = MutableLiveData() val saveId = MutableLiveData() + val tips = MutableLiveData(tipRepository.getTotalTips()) fun startNewGame(newDifficulty: Difficulty = currentDifficulty): Minefield { clock.reset() @@ -203,6 +206,11 @@ class GameViewModel( } } + suspend fun loadGame(): Minefield { + val currentLevelSetup = levelSetup.value + return currentLevelSetup ?: loadLastGame() + } + fun pauseGame() { if (initialized) { if (gameController.hasMines()) { @@ -245,7 +253,7 @@ class GameViewModel( } } - suspend fun onLongClick(index: Int) { + open suspend fun onLongClick(index: Int) { gameController .longPress(index) .filterNotNull() @@ -260,7 +268,7 @@ class GameViewModel( } } - suspend fun onDoubleClick(index: Int) { + open suspend fun onDoubleClick(index: Int) { gameController .doubleClick(index) .filterNotNull() @@ -271,7 +279,7 @@ class GameViewModel( } } - suspend fun onSingleClick(index: Int) { + open suspend fun onSingleClick(index: Int) { gameController .singleClick(index) .filterNotNull() @@ -354,7 +362,22 @@ class GameViewModel( clock.stop() } - fun revealAllEmptyAreas() = gameController.revealAllEmptyAreas() + fun showAllEmptyAreas() { + gameController.revealAllEmptyAreas() + } + + fun revealRandomMine(): Boolean { + return if (gameController.revealRandomMine()) { + if (tipRepository.removeTip()) { + refreshField() + } + + tips.postValue(tipRepository.getTotalTips()) + true + } else { + false + } + } fun explosionDelay() = if (preferencesRepository.useAnimations()) 750L else 0L @@ -403,6 +426,14 @@ class GameViewModel( } } + fun addNewTip() { + tipRepository.increaseTip() + } + + fun getTips(): Int { + return tipRepository.getTotalTips() + } + fun victory() { gameController.run { analyticsManager.sentEvent( diff --git a/common/src/main/java/dev/lucasnlm/antimine/core/analytics/models/Analytics.kt b/common/src/main/java/dev/lucasnlm/antimine/core/analytics/models/Analytics.kt index 0598c13c..26348485 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/core/analytics/models/Analytics.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/core/analytics/models/Analytics.kt @@ -91,6 +91,10 @@ sealed class Analytics( object OpenThemes : Analytics("Open Themes") + object TutorialStarted : Analytics("Tutorial Started") + + object TutorialCompleted : Analytics("Tutorial Completed") + object OpenAchievements : Analytics("Open Achievements") object OpenLeaderboards : Analytics("Open Leaderboards") @@ -115,5 +119,7 @@ sealed class Analytics( object TapRatingRequest : Analytics("Rating Request") + object UseTip : Analytics("Use Tip") + class TapGameReset(resign: Boolean) : Analytics("Game reset", mapOf("Resign" to resign.toString())) } diff --git a/common/src/main/java/dev/lucasnlm/antimine/core/preferences/IPreferencesRepository.kt b/common/src/main/java/dev/lucasnlm/antimine/core/preferences/IPreferencesRepository.kt index aed957a8..6bfe4ced 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/core/preferences/IPreferencesRepository.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/core/preferences/IPreferencesRepository.kt @@ -16,6 +16,9 @@ interface IPreferencesRepository { fun isFirstUse(): Boolean fun completeFirstUse() + fun isTutorialCompleted(): Boolean + fun completeTutorial() + fun customLongPressTimeout(): Long fun themeId(): Long @@ -40,6 +43,11 @@ interface IPreferencesRepository { fun setShowSupport(show: Boolean) fun showSupport(): Boolean + fun getTips(): Int + fun setTips(tips: Int) + fun getExtraTips(): Int + fun setExtraTips(tips: Int) + fun useFlagAssistant(): Boolean fun useHapticFeedback(): Boolean fun areaSizeMultiplier(): Int diff --git a/common/src/main/java/dev/lucasnlm/antimine/core/preferences/PreferencesRepository.kt b/common/src/main/java/dev/lucasnlm/antimine/core/preferences/PreferencesRepository.kt index 10a407fe..66518c6e 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/core/preferences/PreferencesRepository.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/core/preferences/PreferencesRepository.kt @@ -77,6 +77,14 @@ class PreferencesRepository( preferencesManager.putBoolean(PREFERENCE_FIRST_USE, false) } + override fun isTutorialCompleted(): Boolean { + return preferencesManager.getBoolean(PREFERENCE_TUTORIAL_COMPLETED, false) + } + + override fun completeTutorial() { + preferencesManager.putBoolean(PREFERENCE_TUTORIAL_COMPLETED, true) + } + override fun customLongPressTimeout(): Long = preferencesManager.getInt(PREFERENCE_LONG_PRESS_TIMEOUT, ViewConfiguration.getLongPressTimeout()).toLong() @@ -155,6 +163,10 @@ class PreferencesRepository( if (!preferencesManager.contains(PREFERENCE_LONG_PRESS_TIMEOUT)) { preferencesManager.putInt(PREFERENCE_LONG_PRESS_TIMEOUT, defaultLongPressTimeout) } + + if (preferencesManager.contains(PREFERENCE_FIRST_USE)) { + preferencesManager.putBoolean(PREFERENCE_TUTORIAL_COMPLETED, true) + } } override fun setPremiumFeatures(status: Boolean) { @@ -174,6 +186,22 @@ class PreferencesRepository( return preferencesManager.getBoolean(PREFERENCE_SHOW_SUPPORT, true) } + override fun getTips(): Int { + return preferencesManager.getInt(PREFERENCE_TIPS, 5) + } + + override fun setTips(tips: Int) { + preferencesManager.putInt(PREFERENCE_TIPS, tips) + } + + override fun getExtraTips(): Int { + return preferencesManager.getInt(PREFERENCE_EXTRA_TIPS, 0) + } + + override fun setExtraTips(tips: Int) { + preferencesManager.putInt(PREFERENCE_EXTRA_TIPS, tips) + } + private companion object { private const val PREFERENCE_VIBRATION = "preference_vibration" private const val PREFERENCE_ASSISTANT = "preference_assistant" @@ -192,9 +220,12 @@ class PreferencesRepository( private const val PREFERENCE_PROGRESSIVE_VALUE = "preference_progressive_value" private const val PREFERENCE_LONG_PRESS_TIMEOUT = "preference_long_press_timeout" private const val PREFERENCE_FIRST_USE = "preference_first_use" + private const val PREFERENCE_TUTORIAL_COMPLETED = "preference_tutorial_completed" private const val PREFERENCE_USE_COUNT = "preference_use_count" private const val PREFERENCE_REQUEST_RATING = "preference_request_rating" private const val PREFERENCE_PREMIUM_FEATURES = "preference_premium_features" private const val PREFERENCE_SHOW_SUPPORT = "preference_show_support" + private const val PREFERENCE_TIPS = "preference_current_tips" + private const val PREFERENCE_EXTRA_TIPS = "preference_extra_tips" } } diff --git a/common/src/main/java/dev/lucasnlm/antimine/core/themes/model/Assets.kt b/common/src/main/java/dev/lucasnlm/antimine/core/themes/model/Assets.kt index 7187a10c..3794b29f 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/core/themes/model/Assets.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/core/themes/model/Assets.kt @@ -10,4 +10,5 @@ data class Assets( @DrawableRes val mine: Int, @DrawableRes val mineExploded: Int, @DrawableRes val mineLow: Int, + @DrawableRes val revealed: Int, ) diff --git a/common/src/main/java/dev/lucasnlm/antimine/core/themes/repository/ThemeRepository.kt b/common/src/main/java/dev/lucasnlm/antimine/core/themes/repository/ThemeRepository.kt index a2882be7..1e684285 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/core/themes/repository/ThemeRepository.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/core/themes/repository/ThemeRepository.kt @@ -58,7 +58,8 @@ class ThemeRepository( toolbarMine = R.drawable.mine, mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_red, - mineLow = R.drawable.mine_low + mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed ) private fun fromDefaultPalette(context: Context) = diff --git a/common/src/main/java/dev/lucasnlm/antimine/core/themes/repository/Themes.kt b/common/src/main/java/dev/lucasnlm/antimine/core/themes/repository/Themes.kt index 8814a697..3f012098 100644 --- a/common/src/main/java/dev/lucasnlm/antimine/core/themes/repository/Themes.kt +++ b/common/src/main/java/dev/lucasnlm/antimine/core/themes/repository/Themes.kt @@ -36,6 +36,7 @@ object Themes { mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_red, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ) @@ -69,6 +70,7 @@ object Themes { mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_white, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ) @@ -102,6 +104,7 @@ object Themes { mine = R.drawable.mine_low, mineExploded = R.drawable.mine_low, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ) @@ -139,6 +142,7 @@ object Themes { mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_white, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -171,6 +175,7 @@ object Themes { mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_red, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -203,6 +208,7 @@ object Themes { mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_red, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -235,6 +241,7 @@ object Themes { mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_red, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -267,6 +274,7 @@ object Themes { mine = R.drawable.mine_white, mineExploded = R.drawable.mine_white, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_black, ) ), AppTheme( @@ -299,6 +307,7 @@ object Themes { mine = R.drawable.mine_pink, mineExploded = R.drawable.mine_pink_exploded, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -331,6 +340,7 @@ object Themes { mine = R.drawable.mine_pink, mineExploded = R.drawable.mine_pink_exploded, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -362,7 +372,8 @@ object Themes { toolbarMine = R.drawable.mine_low, mine = R.drawable.mine, mineExploded = R.drawable.mine_pink_exploded, - mineLow = R.drawable.mine_low + mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -394,7 +405,8 @@ object Themes { toolbarMine = R.drawable.mine_low, mine = R.drawable.mine, mineExploded = R.drawable.mine_pink_exploded, - mineLow = R.drawable.mine_low + mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -427,6 +439,7 @@ object Themes { mine = R.drawable.mine, mineExploded = R.drawable.mine_pink_exploded, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -458,7 +471,8 @@ object Themes { toolbarMine = R.drawable.mine_low, mine = R.drawable.mine, mineExploded = R.drawable.mine_pink_exploded, - mineLow = R.drawable.mine_low + mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_white, ) ), AppTheme( @@ -491,6 +505,7 @@ object Themes { mine = R.drawable.mine_white, mineExploded = R.drawable.mine_white, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_black, ) ), AppTheme( @@ -522,7 +537,8 @@ object Themes { toolbarMine = R.drawable.mine_low, mine = R.drawable.mine_white, mineExploded = R.drawable.mine_white, - mineLow = R.drawable.mine_low + mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_black, ) ), AppTheme( @@ -555,6 +571,7 @@ object Themes { mine = R.drawable.mine_white, mineExploded = R.drawable.mine_white, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_black, ) ), AppTheme( @@ -587,6 +604,7 @@ object Themes { mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_red, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_black, ) ), AppTheme( @@ -619,6 +637,7 @@ object Themes { mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_red, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_black, ) ), AppTheme( @@ -645,12 +664,13 @@ object Themes { ), assets = Assets( wrongFlag = R.drawable.red_flag, - flag = R.drawable.flag, + flag = R.drawable.flag_black, questionMark = R.drawable.question, toolbarMine = R.drawable.mine, mine = R.drawable.mine, mineExploded = R.drawable.mine_exploded_red, mineLow = R.drawable.mine_low, + revealed = R.drawable.mine_revealed_black, ) ) ) diff --git a/common/src/main/res/drawable-night/mine_revealed.xml b/common/src/main/res/drawable-night/mine_revealed.xml new file mode 100755 index 00000000..8ecdebb3 --- /dev/null +++ b/common/src/main/res/drawable-night/mine_revealed.xml @@ -0,0 +1,9 @@ + + + diff --git a/common/src/main/res/drawable/mine_revealed.xml b/common/src/main/res/drawable/mine_revealed.xml new file mode 100755 index 00000000..91df6a45 --- /dev/null +++ b/common/src/main/res/drawable/mine_revealed.xml @@ -0,0 +1,9 @@ + + + diff --git a/common/src/main/res/drawable/mine_revealed_black.xml b/common/src/main/res/drawable/mine_revealed_black.xml new file mode 100755 index 00000000..8ecdebb3 --- /dev/null +++ b/common/src/main/res/drawable/mine_revealed_black.xml @@ -0,0 +1,9 @@ + + + diff --git a/common/src/main/res/drawable/mine_revealed_white.xml b/common/src/main/res/drawable/mine_revealed_white.xml new file mode 100755 index 00000000..91df6a45 --- /dev/null +++ b/common/src/main/res/drawable/mine_revealed_white.xml @@ -0,0 +1,9 @@ + + + diff --git a/common/src/main/res/layout-watch/fragment_level.xml b/common/src/main/res/layout-watch/fragment_level.xml index cb88f8cd..ccf1355c 100644 --- a/common/src/main/res/layout-watch/fragment_level.xml +++ b/common/src/main/res/layout-watch/fragment_level.xml @@ -13,7 +13,6 @@ android:fadeScrollbars="true" android:overScrollMode="never" android:padding="78dp" - android:scrollbars="none" - android:alpha="0" /> + android:scrollbars="none" /> diff --git a/common/src/main/res/layout/fragment_level.xml b/common/src/main/res/layout/fragment_level.xml index ac9229bb..f8762a42 100644 --- a/common/src/main/res/layout/fragment_level.xml +++ b/common/src/main/res/layout/fragment_level.xml @@ -15,7 +15,6 @@ android:fadeScrollbars="true" android:importantForAccessibility="no" android:overScrollMode="never" - android:scrollbars="none" - android:alpha="0" /> + android:scrollbars="none" /> diff --git a/common/src/main/res/values-af-rZA/strings.xml b/common/src/main/res/values-af-rZA/strings.xml index 7e9b5a57..ae223597 100644 --- a/common/src/main/res/values-af-rZA/strings.xml +++ b/common/src/main/res/values-af-rZA/strings.xml @@ -2,6 +2,19 @@ Antimine Verwyder versteekte myne van \'n mynveld. + Handleiding + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Speletjies Vorige Speletjies Moeilikheidsgraad @@ -21,20 +34,20 @@ Hoogte Myne Wil u \'n nuwe spel begin met hierdie kaart? - Show Licenses + Wys lisensies Wil u \'n nuwe spel begin met hierdie kaart? %d myne - Game Time + Speeltyd Myne Algemeen - Gameplay + Spel Toeganklikheid Grootte Stelsel Terugvoer Ondersteun ons! - With your help, we\'ll be able to implement new features and keep our project active. - To support + Met u hulp kan ons nuwe funksies implementeer en ons projek aktief hou. + Ondersteuning If you like this game, please give us a feedback. It will help us a lot. This game uses the following third parties software: This game was translated by the following people: @@ -43,11 +56,11 @@ Oorwinnings Jy het verloor! Nederlae - Good luck on your next game. - You finished the minefield in %1$d seconds. - Failed to share + Sterkte met u volgende wedstryd. + U het die wedstryd binne %1$d sekondes voltooi. + Kon nie deel nie Weergawe %1$s - Sound Effects + Byklanke Is jy seker? Aktiveer outomatiese plasing van vlaggies Open Areas @@ -55,16 +68,19 @@ Average Time Werkverrigting OK - Use Question Mark + Gebruik die vraagteken Kontrole - Single Click - Double Click + Enkele tik + Dubbeltik Lang-druk Maak oop Merk Weer probeer Leeg + Onmoontlik om dit nou te doen + U het ontvang: +%1$d + Vir meer hulp, moet u \'n wedstryd wen. Onbekende fout. Leierbord Kanselleer @@ -72,7 +88,7 @@ Ja Ontsluit Prestasies - Nee + Nee Algemeen Bron Kode Vertalings @@ -91,7 +107,7 @@ Verwyder alles Temas Încearcă - Delete all events permanently. + Vee alle gebeure permanent uit. All mines were disabled. Covered area Marked area @@ -103,4 +119,5 @@ Flag placed! Flag removed! Verwyder Advertensies + Help diff --git a/common/src/main/res/values-ar-rSA/strings.xml b/common/src/main/res/values-ar-rSA/strings.xml index 5a7d5ee3..8f10e5bd 100644 --- a/common/src/main/res/values-ar-rSA/strings.xml +++ b/common/src/main/res/values-ar-rSA/strings.xml @@ -2,6 +2,19 @@ Antimine يجب عليك مسح لوحة مستطيلة تحتوي على ألغام مخفية دون تفجير أي منها. + الدورة التعليمية + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. ألعاب الألعاب السابقة الصعوبة @@ -65,6 +78,9 @@ علم إعادة المحاولة فارغ + من المستحيل القيام بذلك الآن + لقد استملت مبلغ: +%1$d + للمزيد من المساعدة، يجب أن تفوز بلعبة. خطأ غير معروف. المتصدرين الغاء @@ -72,7 +88,7 @@ نعم فتح القفل الإنجازات - لا + لا عام رمز المصدر الترجمة @@ -103,4 +119,5 @@ وضع العلم! إزالة العلم! إزالة الإعلانات + مساعدة diff --git a/common/src/main/res/values-bg-rBG/strings.xml b/common/src/main/res/values-bg-rBG/strings.xml index 572095bd..b8d9f743 100644 --- a/common/src/main/res/values-bg-rBG/strings.xml +++ b/common/src/main/res/values-bg-rBG/strings.xml @@ -2,6 +2,19 @@ Antimine Вие трябва да изчистите квадратна дъска която има скрити мини без да взривите нито една от тях. + Туториал + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Игри Предишни Игри Трудност @@ -65,6 +78,9 @@ Флаг Опитайте отново Празно + Невъзможно е да се направи това сега + Получихте: +%1$d + За повече помощ трябва да спечелите игра. Неизвестна грешка. Класиране Отказ @@ -72,7 +88,7 @@ Да Отключи Постижения - Не + Не Основни Програмен Код Превод @@ -103,4 +119,5 @@ Флагът е сложен! Глагът е премахнат! Премахване на реклами + Помощ diff --git a/common/src/main/res/values-ca-rES/strings.xml b/common/src/main/res/values-ca-rES/strings.xml index 9675febe..6474fab9 100644 --- a/common/src/main/res/values-ca-rES/strings.xml +++ b/common/src/main/res/values-ca-rES/strings.xml @@ -2,6 +2,19 @@ Antimines Has de netejar un tauler rectangular amb mines amagades sense detonar-ne cap. + Tutorial + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Jocs Partides prèvies Camp de mines @@ -65,6 +78,9 @@ Bandera Reintenta Buida + És impossible fer-ho ara + Heu rebut: +%1$d + Per obtenir més ajuda, heu de guanyar una partida. Error desconegut. Llista de líders Cancel•lar @@ -72,7 +88,7 @@ Desbloqueja Assoliments - No + No General Codi font Traduccions @@ -103,4 +119,5 @@ Bandera col·locada! Has tret la bandera! Elimina els anuncis + Ajuda diff --git a/common/src/main/res/values-cs-rCZ/strings.xml b/common/src/main/res/values-cs-rCZ/strings.xml index d4bc7623..e951e1d7 100644 --- a/common/src/main/res/values-cs-rCZ/strings.xml +++ b/common/src/main/res/values-cs-rCZ/strings.xml @@ -2,6 +2,19 @@ Anti-Mine Musíte vyčistit obdélníkovou desku obsahující skryté miny, aniž by kterákoliv z nich vybuchla. + Tutoriál + Tutoriál dokončen + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Hry Předchozí hry Obtížnost @@ -65,6 +78,9 @@ Vlajka Opakovat Prázdné + Není možné to nyní udělat + Obdrželi jste: +%1$d + Pro další pomoc musíte vyhrát hru. Neznámá chyba. Žebříčky Zrušit @@ -72,7 +88,7 @@ Ano Odemknout Úspěchy - Ne + Ne Obecné Zdrojový kód Překlad @@ -103,4 +119,5 @@ Vlajka umístěna! Vlajka odstraněna! Odstranit reklamy + Nápověda diff --git a/common/src/main/res/values-da-rDK/strings.xml b/common/src/main/res/values-da-rDK/strings.xml index 93f948ad..1c9bec6d 100644 --- a/common/src/main/res/values-da-rDK/strings.xml +++ b/common/src/main/res/values-da-rDK/strings.xml @@ -2,97 +2,113 @@ Antimine You have to clear a rectangular board containing hidden mines without detonating any of them. - Games - Previous Games - Difficulty + Vejledning + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. + Spil + Forrige Spil + Sværhedsgrad Standard - Beginner - Intermediate - Expert - Open + Begynder + Mellemliggende + Ekspert + Åbn Settings - Animations - Haptic Feedback - About - Statistics - Custom + Animationer + Haptisk Feedback + Om + Statistik + Tilpasset Start - Width - Height + Bredde + Højde Mines - If you start a new game, your current progress will be lost. - Show Licenses - Do you want to start a new game? - %d mines - Game Time + Hvis du starter et nyt spil, vil dit nuværende fremskridt gå tabt. + Vis Licenser + Vil du starte et nyt spil? + %d miner + Spil Tid Mine - General + Generelt Gameplay - Accessibility - Size + Tilgængelighed + Størrelse System - Feedback - Support us! - With your help, we\'ll be able to implement new features and keep our project active. - To support - If you like this game, please give us a feedback. It will help us a lot. - This game uses the following third parties software: - This game was translated by the following people: - Failed to sign in. Please check your network connection and try again. - You won! - Victories - You lost! - Defeats - Good luck on your next game. - You finished the minefield in %1$d seconds. - Failed to share + Tilbagemelding + Støt os! + Med din hjælp vil vi være i stand til at implementere nye funktioner og holde vores projekt aktivt. + Støtte + Hvis du kan lide dette spil, bedes du give os en feedback. Det vil hjælpe os meget. + Dette spil bruger følgende tredjepartssoftware: + Dette spil blev oversat af følgende personer: + Kan ikke forbinde. Tjek venligst din netværksforbindelse, og prøv igen. + Du vandt! + Sejre + Du tabte! + Nederlag + Held og lykke med dit næste spil. + Du færdiggjorde minefeltet om %1$d sekunder. + Mislykkedes at dele Version %1$s - Sound Effects - Are you sure? - Enable automatic placing of flags - Open Areas - Total Time - Average Time - Performance + Lyd effekter + Er du sikker? + Aktivér automatisk placering af flag + Åbne Områder + Total Tid + Gennemsnitlig Tid + Ydeevne OK - Use Question Mark + Brug flag med spørgsmålstegn Controls - Single Click - Double Click - Long Press - Open + Enkelt tryk + Dobbeltklik + Langt tryk + Åbn Flag - Retry - Empty - Unknown error. - Leaderboards - Cancel - Resume - Yes - Unlock + Forsøg igen + Tomt + Umuligt at gøre det nu + Du har modtaget: +%1$d + For mere hjælp, skal du vinde et spil. + Ukendt fejl. + Turneringsskema + Annuller + Fortsæt + Ja + Lås op Achievements - No - General - Source Code - Translation - Licenses - Google Play Games - Connect - Connecting… - Disconnect - Disconnected - New Game - Share - Share… - No internet connection. - Open Menu - Close Menu - Delete all - Themes - Try It - Delete all events permanently. - All mines were disabled. + Nej + Generelt + Kildekode + Oversættelser + Licenser + Google Play Spil + Tilslut + Tilslutter… + Afbryd + Afbrudt + Nyt Spil + Del + Del… + Ingen internetforbindelse. + Åbn Menu + Luk Menu + Slet alle + Temaer + Prøv det + Slet alle begivenheder permanent. + Alle miner blev deaktiveret. Covered area Marked area Doubtful area @@ -102,5 +118,6 @@ You exploded a mine! Flag placed! Flag removed! - Remove Ads + Fjern annoncer + Hjælp diff --git a/common/src/main/res/values-de-rDE/strings.xml b/common/src/main/res/values-de-rDE/strings.xml index de048c80..0c53c4a9 100644 --- a/common/src/main/res/values-de-rDE/strings.xml +++ b/common/src/main/res/values-de-rDE/strings.xml @@ -2,6 +2,19 @@ Antimine Du musst ein rechteckiges Spielfeld, das versteckte Minen enthält, räumen, ohne irgendeine davon zur Explosion zu bringen. + Anleitung + Anleitung Abgeschlossen + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Spiele Vorherige Spiele Schwierigkeitsgrad @@ -65,6 +78,9 @@ Flagge Erneut versuchen Leer + Das kann jetzt nicht gemacht werden + Du hast erhalten: +%1$d + Für mehr Hilfe musst du ein Spiel gewinnen. Unbekannter Fehler. Ranglisten Abbrechen @@ -72,7 +88,7 @@ Ja Entsperren Erfolge - Nein + Nein Allgemein Quellcode Übersetzung @@ -103,4 +119,5 @@ Markierung platziert! Markierung entfernt! Werbung entfernen + Hilfe diff --git a/common/src/main/res/values-el-rGR/strings.xml b/common/src/main/res/values-el-rGR/strings.xml index 8a97f999..eda6c48d 100644 --- a/common/src/main/res/values-el-rGR/strings.xml +++ b/common/src/main/res/values-el-rGR/strings.xml @@ -2,6 +2,19 @@ Antimine Πρέπει να καθαρίσετε μια ορθογώνια πλακέτα που περιέχει κρυμμένες \"νάρκες\" χωρίς να πυροδοτήσετε καμία από αυτές. + Οδηγίες + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Παιχνίδια Προηγούμενα Παιχνίδια Δυσκολία @@ -65,6 +78,9 @@ Σημαία Ξαναδοκιμάστε Κενό + Αδύνατο να το κάνουμε τώρα + Εχεις λάβει: +%1$d + Για περισσότερη βοήθεια, πρέπει να κερδίσετε ένα παιχνίδι. Άγνωστο σφάλμα. Πίνακες κατάταξης Ακύρωση @@ -72,7 +88,7 @@ Ναι Ξεκλείδωμα Επιτεύγματα - Όχι + Όχι Γενικά Πηγαίος Κώδικας Μετάφραση @@ -103,4 +119,5 @@ Η Σημαία τοποθετήθηκε! Η σημαία αφαιρέθηκε! Κατάργηση διαφημίσεων + Βοήθεια diff --git a/common/src/main/res/values-en-rUS/strings.xml b/common/src/main/res/values-en-rUS/strings.xml index 053d5f57..7abb104b 100644 --- a/common/src/main/res/values-en-rUS/strings.xml +++ b/common/src/main/res/values-en-rUS/strings.xml @@ -2,6 +2,19 @@ Antimine You have to clear a rectangular board containing hidden mines without detonating any of them. + Tutorial + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Games Previous Games Difficulty @@ -65,6 +78,9 @@ Flag Retry Empty + Impossible to do that now + You have received: %1$d + For more Help, you must win a game. Unknown error. Leaderboards Cancel @@ -72,7 +88,7 @@ Yes Unlock Achievements - No + No General Source Code Translation @@ -103,4 +119,5 @@ Flag placed! Flag removed! Remove Ads + Help diff --git a/common/src/main/res/values-es-rES/strings.xml b/common/src/main/res/values-es-rES/strings.xml index 1f63fcfc..e6027389 100644 --- a/common/src/main/res/values-es-rES/strings.xml +++ b/common/src/main/res/values-es-rES/strings.xml @@ -2,6 +2,19 @@ Anti-Mina Usted tiene que limpiar un tablero cuadrado que contiene minas escondidas sin detonarlas. + Tutorial + ¡Tutorial Completado! + Puedes empezar por %1$s en cualquier cuadrado aleatorio. + Los números indican cuántas minas están adyacentes al cuadrado destacado. + Por lo tanto, si la esquina 1️⃣ tiene sólo un cuadrado adyacente, debe ser una mina. %1$s para marcarlo. + El siguiente cuadrado sin abrir no tiene una mina porque el número es 1️⃣ , no 2️⃣ . + Puedes abrirlo com %1$s. + Puedes abrir todos los demás lugares sin abrir adyacentes a 1️⃣ . Sabes dónde está la mina adyacente. + Puede abrir todos los otros cuadrados cerrados y adyacentes a 1️⃣ . + Sabemos dónde está una de las minas, así que sólo hay una posibilidad para la otra. + %1$s para marcar la mina adyacente a 2️⃣ . + Verifique el número resaltado. + %1$s para abrir o %2$s para marcar. Juegos Juegos anteriores Dificultad @@ -65,6 +78,9 @@ Bandera Reintentar Vacío + Imposible hacer eso ahora + Has recibido: +%1$d + Para obtener más Ayuda, debes ganar una partida. Error desconocido. Ránking Cancelar @@ -72,7 +88,7 @@ Desbloquear Logros - No + No General Código Fuente Traducción @@ -103,4 +119,5 @@ ¡Bandera colocada! ¡Bandera eliminada! Eliminar anuncios + Ayuda diff --git a/common/src/main/res/values-fi-rFI/strings.xml b/common/src/main/res/values-fi-rFI/strings.xml index ee053a54..7e03dfb9 100644 --- a/common/src/main/res/values-fi-rFI/strings.xml +++ b/common/src/main/res/values-fi-rFI/strings.xml @@ -2,6 +2,19 @@ Antimine Sinun täytyy tyhjentää piileskeleviä miinoja sisältävä suorakulmainen taulu räjäyttämättä miinoja kertaakaan. + Opetusohjelma + Opetuspeli Suoritettu + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Pelit Edelliset pelit Vaikeustaso @@ -65,6 +78,9 @@ Lippu Yritä uudelleen Tyhjä + Ei mahdollista tehdä sitä nyt + Olet saanut: +%1$d + Saat enemmän apua, sinun täytyy voittaa pelin. Tuntematon virhe. Tulostaulukot Peruuta @@ -72,7 +88,7 @@ Kyllä Avaa Saavutukset - No + Ei Yleiset Lähdekoodi Käännökset @@ -103,4 +119,5 @@ Lippu asetettu! Lippu poistettu! Poista Mainokset + Apua diff --git a/common/src/main/res/values-fr-rFR/strings.xml b/common/src/main/res/values-fr-rFR/strings.xml index 2f772524..2bd90975 100644 --- a/common/src/main/res/values-fr-rFR/strings.xml +++ b/common/src/main/res/values-fr-rFR/strings.xml @@ -2,6 +2,19 @@ Anti-Mine Vous devez vider un tableau rectangulaire contenant des mines cachées sans en détonner. + Tutoriel + Tutoriel Terminé + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Jeux Parties précédentes Difficulté @@ -65,6 +78,9 @@ Drapeau Réessayer Vide + Impossible de faire cela maintenant + Vous avez reçu : +%1$d + Pour plus d\'Aide, vous devez gagner une partie. Erreur inconnue. Classements Annuler @@ -72,7 +88,7 @@ Oui Déverrouiller Succès - Non + Non Général Code source Traduction @@ -103,4 +119,5 @@ Drapeau placé ! Drapeau retiré ! Supprimer les publicités + Aide diff --git a/common/src/main/res/values-hi-rIN/strings.xml b/common/src/main/res/values-hi-rIN/strings.xml index c8593137..8a592eb6 100644 --- a/common/src/main/res/values-hi-rIN/strings.xml +++ b/common/src/main/res/values-hi-rIN/strings.xml @@ -2,6 +2,19 @@ Antimine लोकप्रिय लॉजिक पजल माइन्सवीपर. बोर्ड से माइंस साफ करता है वर्ग से संकेत को प्रयोग करते हुए जिसे आपने पहले से खोला है. + ट्यूटोरियल + ट्यूटोरियल पूरा हुआ + आप किसी भी यादृच्छिक क्षेत्र पर %1$s द्वारा शुरू कर सकते हैं। + संख्या इंगित करती है कि कितने खदान हाइलाइट किए गए क्षेत्र से सटे हैं। + इसलिए, यदि कोने 1, में केवल एक निकट क्षेत्र है, तो यह एक खदान होना चाहिए। इसे चिह्नित करने के लिए %1$s + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. गेम्स पिछला गेम्स कठिनाई @@ -32,9 +45,9 @@ आकार प्रणाली फीडबैक - हमें सपॉर्ट कीजिये! - With your help, we\'ll be able to implement new features and keep our project active. - To support + हमें सहयोग दीजिये! + आपकी मदद से, हम नई सुविधाओं को लागू करने और अपनी परियोजना को सक्रिय रखने में सक्षम होंगे। + हमें सहयोग दीजिये If you like this game, please give us a feedback. It will help us a lot. This game uses the following third parties software: This game was translated by the following people: @@ -65,6 +78,9 @@ ध्वज फिर प्रयास करें खाली + Impossible to do that now + You have received: +%1$d + For more Help, you must win a game. अज्ञात त्रुटि। लीडरबोर्ड रद्द करें @@ -72,7 +88,7 @@ हाँ अनलॉक करें उपलब्धियां - नहीं + नहीं सामान्य सोर्स कोड अनुवाद @@ -103,4 +119,5 @@ Flag placed! Flag removed! विज्ञापन निकालें + मदद diff --git a/common/src/main/res/values-hu-rHU/strings.xml b/common/src/main/res/values-hu-rHU/strings.xml index e629818e..570709d0 100644 --- a/common/src/main/res/values-hu-rHU/strings.xml +++ b/common/src/main/res/values-hu-rHU/strings.xml @@ -2,6 +2,19 @@ Antimine Meg kell tisztítanod egy rejtett aknákkal teli, négyszögletes pályát anélkül, hogy akár egyet is felrobbantanál. + Tutorial + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Játékok Előző játékok Nehézség @@ -65,6 +78,9 @@ Zászló Újra Üres + Most lehetetlen ezt megtenni + Kapott összeg: +%1$d + További segítségért meg kell nyernie egy játékot. Ismeretlen hiba. Ranglista Mégse @@ -72,7 +88,7 @@ Igen Zárolás feloldása Trófeák - Nem + Nem Általános Forráskód Fordítás @@ -103,4 +119,5 @@ Zászló elhelyezve! Zászló eltávolítva! Reklámok eltávolítása + Segítség diff --git a/common/src/main/res/values-in-rID/strings.xml b/common/src/main/res/values-in-rID/strings.xml index b435fe52..8d257b84 100644 --- a/common/src/main/res/values-in-rID/strings.xml +++ b/common/src/main/res/values-in-rID/strings.xml @@ -2,6 +2,19 @@ Antimine Kosongkan ranjau tersembunyi dari medan ranjau + Tutorial + Tutorial selesai + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Permainan Tantangan Sebelumnya Kesulitan @@ -65,6 +78,9 @@ Bendera Ulangi Kosong + Tidak mungkin melakukan itu sekarang + Anda telah menerima: +%1$d + Untuk Bantuan lebih lanjut, Anda harus memenangkan permainan. Galat tak diketahui. Papan Peringkat Batal @@ -72,7 +88,7 @@ Ya Buka Prestasi - Tidak + Tidak Umum Kode sumber Alih Bahasa @@ -103,4 +119,5 @@ Bendera diletakkan! Bendera dihapus! Hapus Iklan + Bantuan diff --git a/common/src/main/res/values-it-rIT/strings.xml b/common/src/main/res/values-it-rIT/strings.xml index 8086c734..99f2d3a5 100644 --- a/common/src/main/res/values-it-rIT/strings.xml +++ b/common/src/main/res/values-it-rIT/strings.xml @@ -2,6 +2,19 @@ Antimine L\'obbiettivo del gioco è ripulire un campo rettangolare che contiene mine nascoste senza detonarne nessuna. + Tutorial + Tutorial Completato + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Giochi Partite Precedenti Difficoltà @@ -65,6 +78,9 @@ Bandiera Riprova Vuoto + Impossibile farlo ora + Hai ricevuto: +%1$d + Per ulteriori aiuti, devi vincere una partita. Errore sconosciuto. Classifiche Annulla @@ -72,7 +88,7 @@ Sblocca Progressi - No + No Generale Codice sorgente Traduzione @@ -103,4 +119,5 @@ Bandiera piazzata! Bandiera rimossa! Rimuovi gli Annunci + Help diff --git a/common/src/main/res/values-iw-rIL/strings.xml b/common/src/main/res/values-iw-rIL/strings.xml index dd589a54..0cd117b7 100644 --- a/common/src/main/res/values-iw-rIL/strings.xml +++ b/common/src/main/res/values-iw-rIL/strings.xml @@ -1,106 +1,123 @@ Antimine - You have to clear a rectangular board containing hidden mines without detonating any of them. - Games - Previous Games - Difficulty - Standard - Beginner - Intermediate - Expert - Open - Settings - Animations - Haptic Feedback - About - Statistics - Custom - Start - Width - Height - Mines - If you start a new game, your current progress will be lost. - Show Licenses - Do you want to start a new game? - %d mines - Game Time - Mine - General - Gameplay - Accessibility - Size - System - Feedback - Support us! - With your help, we\'ll be able to implement new features and keep our project active. - To support + צריך לנקות את הלוח הריבועי המכיל מוקשים נסתרים מבלי לפוצץ אף אחד מהם. + הדרכה + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. + משחקים + משחקים קודמים + רמת קושי + רגיל + רמת התחלה + רמה בינונית + רמה גבוהה + פתיחה + הגדרות + אנימציות + משוב רטט + אודות + סטטיסטיקה + התאמה אישית + התחלה + רוחב + גובה + מוקשים + כאשר יתחיל משחק חדש, ההתקדמות הנוכחית שלך תיעלם. + הצג רשיונות + האם ברצונך להתחיל משחק חדש? + %d מוקשים + זמן משחק + מוקש + כללי + משחק + נגישות + גודל + מערכת + משוב + תמכו בנו! + עם עזרתך, נוכל להוסיף דברים חדשים ולשמור על הפרויקט שלנו פעיל. + לתמוך If you like this game, please give us a feedback. It will help us a lot. This game uses the following third parties software: This game was translated by the following people: - Failed to sign in. Please check your network connection and try again. - You won! - Victories - You lost! - Defeats - Good luck on your next game. - You finished the minefield in %1$d seconds. - Failed to share - Version %1$s - Sound Effects - Are you sure? - Enable automatic placing of flags + אין אפשרות להתחבר. בדוק את חיבור הרשת ונסה שוב. + ניצחת! + נצחונות + הפסדת! + הפסדים + בהצלחה במשחק הבא. + סיימת את שדה המוקשים ב%1$d שניות. + השיתוף נכשל + גרסה %1$s + אפקטי צליל + את/ה בטוח/ה? + מיקום דגלים אוטומטי Open Areas - Total Time - Average Time - Performance + זמן כולל + זמן ממוצע + ביצועים OK - Use Question Mark - Controls - - Single Click - Double Click - Long Press - Open - Flag - Retry - Empty - Unknown error. - Leaderboards - Cancel - Resume - Yes - Unlock - Achievements - No - General - Source Code - Translation - Licenses + סימן שאלה + פקדים + 🠨 + לחיצה בודדת + לחיצה כפולה + לחץ לחיצה ארוכה + פתוח + דגל + נסה שנית + ריק + אי אפשר לעשות את זה עכשיו + קיבלת: +%1$d + לקבלת עזרה נוספת, עליך לנצח במשחק. + שגיאה לא ידועה. + לוח תוצאות + ביטול + לְהַמשִׁיך + כן + בטל את הנעילה + הישגים + לא + כללי + קוד מקור + תרגומים + רשיונות Google Play Games - Connect - Connecting… - Disconnect - Disconnected - New Game - Share - Share… - No internet connection. - Open Menu - Close Menu - Delete all - Themes - Try It + התחבר + מתחבר… + נתק + מנותק + משחק חדש + שותף + שותף… + אין חיבור לאינטרנט. + פתח תפריט + סגור תפריט + מחק הכל + ערכות נושא + נסה את זה Delete all events permanently. All mines were disabled. Covered area Marked area Doubtful area Wrongly marked area - Exploded Mine - Game Started - You exploded a mine! - Flag placed! - Flag removed! - Remove Ads + מוקש מפוצץ + המשחק החל + פוצצת מוקש! + דגל הונח! + דגל הוסר! + הסרת פרסומות + עזרה diff --git a/common/src/main/res/values-ja-rJP/strings.xml b/common/src/main/res/values-ja-rJP/strings.xml index eb0ddc9f..c455a15f 100644 --- a/common/src/main/res/values-ja-rJP/strings.xml +++ b/common/src/main/res/values-ja-rJP/strings.xml @@ -2,6 +2,19 @@ Antimine 地雷が隠された長方形から1つも爆発させることなくクリアしましょう。 + チュートリアル + チュートリアル完了 + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. ゲーム回数 前のゲーム 難易度 @@ -65,6 +78,9 @@ リトライ + 今それを行うことは不可能です + 受け取りました: +%1$d + ヘルプを増やすには、ゲームに勝つ必要があります。 不明なエラーです。 リーダーボード キャンセル @@ -72,7 +88,7 @@ はい ロック解除 達成 - いいえ + いいえ 全般 ソースコード 翻訳 @@ -103,4 +119,5 @@ フラグを立てました! フラグを消しました! 広告を削除 + ヘルプ diff --git a/common/src/main/res/values-ko-rKR/strings.xml b/common/src/main/res/values-ko-rKR/strings.xml index faa58d8f..45cc7c52 100644 --- a/common/src/main/res/values-ko-rKR/strings.xml +++ b/common/src/main/res/values-ko-rKR/strings.xml @@ -1,106 +1,123 @@ Antimine - You have to clear a rectangular board containing hidden mines without detonating any of them. - Games - Previous Games + 당신은 이 사각형 게임판에 숨겨진 지뢰를 하나도 터뜨리지 않고 게임을 끝내야 합니다. + 튜토리얼 + 튜토리얼 완료 + 아무 곳에나 %1$s해서 게임을 시작하세요. + 이 숫자는 선택한 곳의 인접한 구간에 얼마나 많은 지뢰가 있는지 나타냅니다. + 따라서, 1️⃣ 으로 표시된 부분에 인접한 구역이 한 군데밖에 없다면, 그것은 지뢰일 것입니다. %1$s해서 깃발을 세우세요. + 그 다음 열리지 않은 구역은 숫자가 2️⃣ 가 아닌 1️⃣ 이기에, 지뢰를 가지고 있지 않습니다. + %1$s해서 그 칸을 여세요. + 이 1️⃣ 이라 표시된 구역에 똑같은 상황이 나왔습니다. 인접한 지뢰가 어디 있는지 확인할 수 있을겁니다. + 1️⃣ 근처의 모든 칸을 열어도 됩니다. + 이미 지뢰 하나는 어디 있는지 알고 있습니다. 그러니 나머지 하나가 있을 경우의 수는 단 한가지입니다. + %1$s해서 2️⃣ 에 인접한 칸에 있는 지뢰에 깃발을 세우세요. + 표시된 숫자를 확인하세요. + 해당 구역이 안전하다고 생각하면 %1$s하고, 해당 구역에 지뢰가 있다고 생각하면 %2$s하세요. + 게임 + 이전 게임 난이도 기본 초급 중급 고급 - Open + 오픈 설정 - Animations - Haptic Feedback - About + 애니메이션 + 햅틱 피드백 + 정보 통계 - Custom + 사용자 지정 시작 - Width - Height + 가로 + 세로 지뢰 수 - If you start a new game, your current progress will be lost. - Show Licenses - Do you want to start a new game? - %d mines - Game Time - Mine - General - Gameplay - Accessibility - Size - System - Feedback - Support us! - With your help, we\'ll be able to implement new features and keep our project active. - To support - If you like this game, please give us a feedback. It will help us a lot. - This game uses the following third parties software: - This game was translated by the following people: - Failed to sign in. Please check your network connection and try again. - You won! - Victories - You lost! - Defeats - Good luck on your next game. - You finished the minefield in %1$d seconds. - Failed to share - Version %1$s - Sound Effects - Are you sure? - Enable automatic placing of flags - Open Areas - Total Time - Average Time - Performance + 새 게임을 시작하면 현재 진행 상황이 손실됩니다. + 저작권 표시 + 새로운 게임을 시작하시겠습니까? + %d 개 + 게임 시간 + 지뢰 + 일반 + 게임 플레이 + 접근성 + 크기 + 시스템 + 피드백 + 우리를 지원해주세요! + 새로운 기능을 구현하고 프로젝트가 계속 진행될 수 있게 도와주세요. + 지원 + 이 게임이 좋으시다면, 피드백을 남겨주세요. 저희에게 많은 도움이 됩니다. + 이 게임은 해당 서드파티 소프트웨어를 사용합니다: + 이 게임은 다음 사람들에 의해 번역되었습니다: + 게임에 접속할 수 없습니다. 네트워크 환경을 확인해고 다시 접속해주세요. + 성공! + 성공 횟수 + 실패! + 실패 횟수 + 다음 판에서는 잘하시길 빕니다. + %1$d초만에 이번 판을 끝내셨습니다. + 공유 실패 + 버전: %1$s + 효과음 + 확실합니까? + 자동으로 깃발 놓기 사용 + 칸 열기 + 총 시간 + 평균 시간 + 퍼포먼스 OK - Use Question Mark - Controls + 물음표 깃발 사용 + 컨트롤 - Single Click - Double Click - Long Press - Open - Flag - Retry - Empty - Unknown error. - Leaderboards - Cancel - Resume - Yes - Unlock - Achievements - No - General - Source Code - Translation - Licenses - Google Play Games - Connect - Connecting… - Disconnect - Disconnected - New Game - Share - Share… - No internet connection. - Open Menu - Close Menu - Delete all - Themes - Try It - Delete all events permanently. - All mines were disabled. - Covered area - Marked area - Doubtful area - Wrongly marked area - Exploded Mine - Game Started - You exploded a mine! - Flag placed! - Flag removed! - Remove Ads + 단일 클릭 + 더블 클릭 + 길게 누르기 + 칸 열기 + 깃발 세우기 + 재시도 + 비어있음 + 지금은 불가능 해 + 당신은 받았습니다: +%1$d + 더 많은 도움을 받으려면 게임에서 승리해야합니다. + 알 수 없는 오류 발생 + 리더보드 + 취소 + 계속하기 + + 잠금 해제 + 도전 과제 + 아니오 + 기본설정 + 소스 코드 + 번역 + 라이선스 + Google Play 게임 + 연결하기 + 연결중… + 연결 해제 + 연결 해제됨 + 새 게임 + 공유하기 + …을 통해 공유하기 + 인터넷에 연결되어 있지 않습니다. + 메뉴 열기 + 메뉴 닫기 + 모두 삭제 + 테마 선택 + 사용해보세요 + 모든 이벤트 알림을 영구히 삭제합니다. + 모든 지뢰를 찾았습니다. + 연 칸 + 깃발 꽂은 칸 + 의심스러운 칸 + 깃발을 잘못 꽂은 칸 + 터진 지뢰 + 게임 시작됨 + 지뢰가 터졌습니다! + 깃발 설치 완료! + 깃발 제거 완료! + 광고 제거 + 도움말 diff --git a/common/src/main/res/values-nl-rNL/strings.xml b/common/src/main/res/values-nl-rNL/strings.xml index b7a0ae88..15fca9f9 100644 --- a/common/src/main/res/values-nl-rNL/strings.xml +++ b/common/src/main/res/values-nl-rNL/strings.xml @@ -2,6 +2,19 @@ Antimijn Je moet een rechthoekig bord met verborgen mijnen verwijderen zonder er een te ontploffen. + Tutorial + Tutorial Voltooid + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Games Vorige games Moeilijkheidsgraad @@ -65,6 +78,9 @@ Vlag Opnieuw Leeg + Onmogelijk om dat nu te doen + Je hebt ontvangen: +%1$d + Voor meer hulp moet je een spel winnen. Onbekende fout. Ranglijsten Anneleren @@ -72,7 +88,7 @@ Ja Ontgrendelen Achievements - Nee + Nee Algemeen Bron code Vertaling @@ -103,4 +119,5 @@ Vlag geplaatst! Vlag verwijderd! Verwijder advertenties + Help diff --git a/common/src/main/res/values-no-rNO/strings.xml b/common/src/main/res/values-no-rNO/strings.xml index c7d444d8..0c138e67 100644 --- a/common/src/main/res/values-no-rNO/strings.xml +++ b/common/src/main/res/values-no-rNO/strings.xml @@ -2,6 +2,19 @@ Antimine Du må fjerne en rektangulær plate med skjulte miner uten å løsne noen av dem. + Tutorial + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Spill Tidligere Spill Vanskelighet @@ -33,8 +46,8 @@ System Ttilbakemelding Støtt oss! - Med din hjelp kan vi implementere nye funksjoner og holde prosjektet vårt aktivt. - Støtte + Med hjelpen din kan vi implementere nye funksjoner og holde prosjektet aktivt. + For å støtte Hvis du liker dette spillet, gi oss en tilbakemelding. Det vil hjelpe oss mye. Dette spillet bruker følgende tredjeparter programvare: Dette spillet ble oversatt av følgende personer: @@ -65,6 +78,9 @@ Flagg Prøv på nytt Tom + Umulig å gjøre det nå + Du har mottatt: +%1$d + For mer hjelp, må du vinne et spill. Ukjent feil. Leaderboards Avbryt @@ -72,7 +88,7 @@ Ja Lås opp Prestasjoner - Nei + Nei Generelt Kildekode Oversettelse @@ -103,4 +119,5 @@ Flagg plassert! Flagg fjernet! Fjern Reklame + Hjelp diff --git a/common/src/main/res/values-pl-rPL/strings.xml b/common/src/main/res/values-pl-rPL/strings.xml index 1b25a9a6..29367bc9 100644 --- a/common/src/main/res/values-pl-rPL/strings.xml +++ b/common/src/main/res/values-pl-rPL/strings.xml @@ -2,6 +2,19 @@ Antimine Znajdź wszystkie ukryte miny znajdujące się na kwadratowej planszy unikając ich detonacji. + Samouczek + Samouczek ukończony + %1$s na dowolnym polu, aby rozpocząć. + Liczby wskazują, jaka ilość min przylega do podświetlonego pola. + Z tego wynika, że jeśli do narożnika 1️⃣ przylega tylko jedno pole, musi znajdować się tam mina. %1$s, aby ją oznaczyć. + Na kolejnym nieodkrytym polu nie znajduje się mina, ponieważ posiada ono liczbę 1️⃣ , a nie 2️⃣ . + Możesz je odkryć przez %1$s. + To samo dzieje się w przypadku tego 1️⃣ . Z pewnością wiesz, gdzie jest przylegająca mina. + Możesz odkryć wszystkie pozostałe pola przylegające do 1️⃣ . + Wiemy już, gdzie jest jedna z min. Istnieje więc tylko jedna możliwa pozycja dla drugiej. + %1$s, aby oznaczyć minę przylegającą do 2️⃣ . + Sprawdź podświetlony numer. + %1$s, aby odkryć lub %2$s, aby oznaczyć. Rozgrywki Poprzednie rozgrywki Rozgrywki @@ -61,10 +74,13 @@ Pojedyncze kliknięcie Podwójne kliknięcie Długie naciśnięcie - Otwórz + Odkryj Oznacz Jeszcze raz Puste + Nie można tego teraz zrobić + Otrzymałeś: +%1$d + Aby uzyskać więcej pomocy, musisz wygrać grę. Nieznany błąd. Tablica wyników Anuluj @@ -72,7 +88,7 @@ Tak Odblokuj Osiągnięcia - Nie + Nie Ogólne Kod źródłowy Tłumaczenie @@ -102,5 +118,6 @@ Zdetonowano minę! Umieszczono flagę! Usunięto flagę! - Usunąć reklamy + Usuń reklamy + Pomoc diff --git a/common/src/main/res/values-pt-rBR/strings.xml b/common/src/main/res/values-pt-rBR/strings.xml index 89407549..3cd11837 100644 --- a/common/src/main/res/values-pt-rBR/strings.xml +++ b/common/src/main/res/values-pt-rBR/strings.xml @@ -2,6 +2,19 @@ Antimine Você deve limpar um campo cheio de mines escondidas sem detonar nenhumas delas. + Tutorial + Tutorial Concluído + Para começar dê um %1$s em qualquer quadrado aleatório. + Os números indicam quantas minas existem adjacentes ao quadrado destacado. + Portanto, se o canto 1️⃣ tem apenas um quadrado adjacente, ele deve ter uma minha. Use %1$s para sinalizá-lo. + O próximo quadrado fechado não tem uma mina porque o número é 1️⃣ , não 2️⃣ . + Você pode abri-lo com %1$s. + O mesmo acontece nesse 1️⃣ . Você sabe onde está a mina adjacente. + Você pode abrir todos os outros quadrados fechados adjacentes a 1️⃣ . + Sabemos onde está uma das minas. Então, há apenas uma possibilidade para a outra. + %1$s para sinalizar a mina adjacente a 2️⃣ . + Verifique o número destacado. + %1$s para abrir ou %2$s para sinalizar. Jogos Jogos anteriores Dificuldade @@ -65,6 +78,9 @@ Bandeira Tentar de novo Vazio + Impossível fazer isso agora + Você recebeu: +%1$d + Para mais Ajuda, você deve ganhar um jogo. Erro Desconhecido. Classificações Cancelar @@ -72,7 +88,7 @@ Sim Desbloquear Conquistas - Não + Não Geral Código fonte Tradução @@ -103,4 +119,5 @@ Bandeira colocada! Bandeira removida! Remover Propagandas + Ajuda diff --git a/common/src/main/res/values-pt-rPT/strings.xml b/common/src/main/res/values-pt-rPT/strings.xml index 2cbb49e2..d30711e8 100644 --- a/common/src/main/res/values-pt-rPT/strings.xml +++ b/common/src/main/res/values-pt-rPT/strings.xml @@ -2,6 +2,19 @@ Antimine Você deve limpar um campo contendo minas escondidas sem detonar nenhuma delas. + Tutorial + Tutorial Completo + Você pode começar por %1$s em qualquer quadrado aleatório. + Os números indicam quantas minas estão adjacentes ao quadrado em destaque. + Portanto, se o canto 1️⃣ tem apenas um quadrado adjacente, ele deve ter uma mina. %1$s para sinalizá-lo. + O próximo quadrado não aberto não tem uma mina, porque o número é 1️⃣ , não 2️⃣ . + Você pode abri-lo com %1$s. + O mesmo acontece neste 1️⃣ . Você sabe onde está a sua mina adjacente. + Você pode abrir todos os outros lugares não abertos adjacentes a 1️⃣ . + Sabemos onde está uma das minas. Então, há apenas uma possibilidade para a outra. + %1$s para sinalizar a mina adjacente a 2️⃣ . + Verifique o número destacado. + %1$s para abrir ou %2$s para marcar. Jogos Jogos anteriores Dificuldade @@ -58,13 +71,16 @@ Usar Ponto de Interrogação Controlos - Clique único - Duplo Clique - Toque longo + Toque Único + Toque Duplo + Toque Longo Abrir Bandeira Tentar Novamente Vazio + Impossível fazer isso agora + Você recebeu: +%1$d + Para mais Ajuda, você deve ganhar um jogo. Erro desconhecido. Líderes Cancelar @@ -72,7 +88,7 @@ Sim Desbloquear Conquistas - Não + Não Geral Código Fonte Tradução @@ -85,7 +101,7 @@ Novo Jogo Compartilhar Compartilhar… - Sem ligação à internet. + Sem ligação à Internet. Abrir o Menu Fechar menu Apagar tudo @@ -103,4 +119,5 @@ Bandeira colocada! Bandeira removida! Remover Anúncios + Ajuda diff --git a/common/src/main/res/values-ro-rRO/strings.xml b/common/src/main/res/values-ro-rRO/strings.xml index f3864a68..f7bd5fe3 100644 --- a/common/src/main/res/values-ro-rRO/strings.xml +++ b/common/src/main/res/values-ro-rRO/strings.xml @@ -2,6 +2,19 @@ Antimine You have to clear a rectangular board containing hidden mines without detonating any of them. + Tutorial + Tutorial completat + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Games Previous Games Dificultate @@ -26,56 +39,59 @@ %d de mine Durata Jocului Mină - General - Gameplay - Accessibility - Size - System - Feedback - Support us! - With your help, we\'ll be able to implement new features and keep our project active. - To support - If you like this game, please give us a feedback. It will help us a lot. + Generalități + Joc + Accesibilitate + Dimensiune + Sistem + Opinii + Susțineți-ne! + Cu ajutorul tău, vom fi capabili să implementăm noi funcții și să menținem proiectul nostru activ. + Suport + Dacă vă place acest joc, vă rugăm să ne dați un feedback. Ne va ajuta foarte mult. This game uses the following third parties software: This game was translated by the following people: Nu se poate conecta. Te rugăm să îți verifici conexiunea la rețea și să încerci din nou. - You won! - Victories - You lost! - Defeats - Good luck on your next game. - You finished the minefield in %1$d seconds. - Failed to share - Version %1$s - Sound Effects - Are you sure? - Enable automatic placing of flags + Aţi câștigat! + Victorii + Aţi pierdut! + Înfrângeri + Mult noroc la următorul joc. + Ai terminat câmpul minat în %1$d secunde. + Share-uirea imaginii a eșuat + Versiunea %1$s + Efecte sonore + Sînteți sigur? + Activează plasarea automată a steagurilor Open Areas - Total Time - Average Time - Performance + Timp total + Timp mediu + Performanță OK - Use Question Mark - Controls + Folosește Semn de Întrebare + Controale - Single Click - Double Click - Long Press - Open - Flag + Atingere simplă + Apasare dubla + Atingeți lung + Deschide + Steag Încearcă din nou - Empty + Gol + Nu se poate face acest lucru acum + Ați primit: +%1$d + Pentru mai mult ajutor, trebuie să câștigi un joc. Eroare necunoscută. Clasamente Anulează Continuă Da - Unlock + Deblochează Achievements - No + Nu General Cod sursă - Translation + Translare Licenţe Jocurile Google Play Conectează-te @@ -88,9 +104,9 @@ Lipseste conexiune la internet. Deschide meniul Închide meniul - Delete all - Themes - Try It + Șterge tot + Teme + Încearcă-l Delete all events permanently. Toate minele au fost dezactivate. Covered area @@ -102,5 +118,6 @@ Ai explodat o mină! Steagul a fost plasat! Steagul a fost eliminat! - Remove Ads + Eliminați Reclamele + Ajutor diff --git a/common/src/main/res/values-ru-rRU/strings.xml b/common/src/main/res/values-ru-rRU/strings.xml index 22ba3269..6ecc784b 100644 --- a/common/src/main/res/values-ru-rRU/strings.xml +++ b/common/src/main/res/values-ru-rRU/strings.xml @@ -2,6 +2,19 @@ Anti-Mine Вам необходимо расчистить прямоугольную площадь со спрятанными минами, не взорвав ни одну из них. + Обучение + Обучение завершено + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Игры Предыдущие игры Сложность @@ -65,6 +78,9 @@ Флаг Повторить Пусто + Невозможно сделать это сейчас + Вы получили: +%1$d + Для получения дополнительной помощи вы должны выиграть игру. Неизвестная ошибка. Списки лидеров Отмена @@ -72,7 +88,7 @@ Да Разблокировать Достижения - Нет + Нет Общее Исходный код Перевод @@ -103,4 +119,5 @@ Флаг установлен! Флаг снят! Удалить рекламу + Помощь diff --git a/common/src/main/res/values-sv-rSE/strings.xml b/common/src/main/res/values-sv-rSE/strings.xml index 48101f94..7627cb54 100644 --- a/common/src/main/res/values-sv-rSE/strings.xml +++ b/common/src/main/res/values-sv-rSE/strings.xml @@ -2,6 +2,19 @@ Antimine You have to clear a rectangular board containing hidden mines without detonating any of them. + Tutorial + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Games Previous Games Difficulty @@ -65,6 +78,9 @@ Flag Retry Empty + Impossible to do that now + You have received: +%1$d + For more Help, you must win a game. Unknown error. Leaderboards Cancel @@ -72,7 +88,7 @@ Yes Unlock Achievements - No + No General Source Code Translation @@ -103,4 +119,5 @@ Flag placed! Flag removed! Remove Ads + Help diff --git a/common/src/main/res/values-th-rTH/strings.xml b/common/src/main/res/values-th-rTH/strings.xml index ee130fbf..a48fa7e6 100644 --- a/common/src/main/res/values-th-rTH/strings.xml +++ b/common/src/main/res/values-th-rTH/strings.xml @@ -2,6 +2,19 @@ Antimine You have to clear a rectangular board containing hidden mines without detonating any of them. + Tutorial + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Games Previous Games Difficulty @@ -65,6 +78,9 @@ Flag Retry Empty + Impossible to do that now + You have received: +%1$d + For more Help, you must win a game. Unknown error. Leaderboards Cancel @@ -72,7 +88,7 @@ Yes Unlock Achievements - No + No General Source Code Translation @@ -103,4 +119,5 @@ Flag placed! Flag removed! Remove Ads + Help diff --git a/common/src/main/res/values-tr-rTR/strings.xml b/common/src/main/res/values-tr-rTR/strings.xml index 7511f587..02db2f48 100644 --- a/common/src/main/res/values-tr-rTR/strings.xml +++ b/common/src/main/res/values-tr-rTR/strings.xml @@ -2,6 +2,19 @@ Anti-Mine Hiçbirini patlatmadan gizli mayın içeren dikdörtgen bir tahtayı temizlemelisiniz. + Eğitim + Eğitim tamamlandı + Rastgele karede %1$s ile başlayabilirsiniz. + Numaralar vurgulanan kareye kaç tane bitişik mayın olduğuna işaret eder. + Bu nedenle, köşe 1 sadece bir bitişik kareye sahipse, bu bir mayın olmalıdır. İşaretlemek için %1$s. + Bir sonraki açılmamış karede mayın yok çünkü sayı 2️⃣değil, 1️⃣. + %1$s ile açabilirsin. + Bu 1️⃣\'de de aynı şey. Bitişik mayının nerede olduğunu biliyorsun. + 1️⃣\'e bitişik bütün açılmamış mayınları açabilirsin. + Mayınların bir tanesinin nerede olduğunu biliyoruz. Böylece diğer mayın için sadece bir olasılık var. + 2️⃣\'ye komşu olan mayını işaretlemek için %1$s. + Vurgulanan numarayı kontrol edin. + Açmak için %1$s ya da işaretlemek için %2$s. Oyunlar Önceki Oyunlar Zorluk @@ -65,6 +78,9 @@ Bayrak Yeniden dene Boş + Bunu şimdi yapmak imkansız + Aldın: +%1$d + Daha fazla Yardım için bir oyun kazanmalısınız. Bilinmeyen hata. Liderlik Tablosu İptal @@ -72,7 +88,7 @@ Evet Kilidini Aç Başarımlar - Hayır + Hayır Genel Kaynak Kodu Çeviri @@ -103,4 +119,5 @@ Bayrak yerleştirildi! Bayrak kaldırıldı! Reklamları kaldır + Yardım diff --git a/common/src/main/res/values-uk-rUA/strings.xml b/common/src/main/res/values-uk-rUA/strings.xml index 3c0946fa..95585e55 100644 --- a/common/src/main/res/values-uk-rUA/strings.xml +++ b/common/src/main/res/values-uk-rUA/strings.xml @@ -2,6 +2,19 @@ Сапер Вам потрібно очистити поле від схованих мін, не детонуючи їх. + Навчальний посібник + Навчання пройдено + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Ігри Попередня гра Складність @@ -65,6 +78,9 @@ Прапор Повторити Пусто + Неможливо зробити це зараз + Ви отримали: +%1$d + Для отримання додаткової допомоги ви повинні виграти гру. Невідома помилка. Таблиця лідерів Відмінити @@ -72,7 +88,7 @@ Так Розблокування Досягнення - Ні + Ні Загальне Вихідний код Переклад @@ -103,4 +119,5 @@ Прапор розміщено! Прапор вилучено! Прибрати рекламу + Help diff --git a/common/src/main/res/values-vi-rVN/strings.xml b/common/src/main/res/values-vi-rVN/strings.xml index 70bbc97f..f61b5bdd 100644 --- a/common/src/main/res/values-vi-rVN/strings.xml +++ b/common/src/main/res/values-vi-rVN/strings.xml @@ -2,6 +2,19 @@ Dò mìn Bạn phải mở tất cả các ô trên một bãi mìn mà không làm nổ cục mìn nào. + Hướng dẫn + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Trò chơi Trò chơi Trước Độ khó @@ -65,6 +78,9 @@ Cờ Thử lại Trống + Không thể làm điều đó bây giờ + Bạn đã nhận được: +%1$d + Để có thêm Trợ giúp, bạn phải thắng một trò chơi. Lỗi không xác định. Bảng xếp hạng Huỷ @@ -72,7 +88,7 @@ Mở khoá Thành tựu - Không + Không Tổng quan Mã nguồn Dịch thuật @@ -103,4 +119,5 @@ Đã cắm cờ! Đã tháo cờ hiệu! Loại bỏ quảng cáo + Trợ Giúp diff --git a/common/src/main/res/values-zh-rCN/strings.xml b/common/src/main/res/values-zh-rCN/strings.xml index 8b691827..534a8fb1 100644 --- a/common/src/main/res/values-zh-rCN/strings.xml +++ b/common/src/main/res/values-zh-rCN/strings.xml @@ -2,6 +2,19 @@ Antimine - 扫雷 你需要清除一个隐藏着地雷的矩形面板,不能使任何地雷爆炸。 + 教学模式 + 已完成的教程 + 你可以在任意的正方形开始 %1$s. + 数字表明有多少地雷与高亮的正方形相邻。 + 因此,如果角 1️⃣ 只有一个相邻的正方形,它必须是一个地雷。 %1$s 来标记它。 + 下一个未打开的方块没有地雷,因为数字是 1️⃣ , 而不是 2️⃣ 。 + 您可以通过 %1$s 打开它。 + 此 1️⃣ 发生同样的情况。您知道其相邻的矿藏在哪里。 + 您可以在 1️⃣ 旁打开所有其他未打开的方块 + 我们知道其中一枚地雷在哪里,因此,另一枚地雷只有一种可能性。 + %1$s 标记附近的 2️⃣ 的矿。 + 检查高亮的数字。 + %1$s 打开或 %2$s 标记。 游戏 上一局 难度 @@ -65,6 +78,9 @@ 标志 重试 + 现在无法做到这一点。 + 您已收到: +%1$d + 为了获得更多帮助,你必须赢得一场游戏。 未知错误。 排行榜 取消 @@ -72,7 +88,7 @@ 解锁​​​​ 成就 - + 通用 源代码 翻译 @@ -103,4 +119,5 @@ 标记已放置! 标记已移除! 去除广告 + 帮助 diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 86b97b3f..8d7462ed 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -1,7 +1,18 @@ - Antimine - You have to clear a rectangular board containing hidden mines without detonating any of them. + Tutorial + Tutorial Completed + You can start by %1$s at any random square. + The numbers indicates how many mines are adjacent to the highlighted square. + Therefore, if the corner 1️⃣ has only one adjacent square, it must be a mine. %1$s to flag it. + The next unopened square doesn\'t have a mine because the number is 1️⃣ , not 2️⃣ . + You can open it by %1$s. + The same happens on this 1️⃣ . You know where its adjacent mine is. + You can open all other unopened squares adjacent to 1️⃣ . + We know where one of the mines are. So, there\'s only one possibility to the other one. + %1$s to flag the mine adjacent to 2️⃣ . + Check the highlighted number. + %1$s to open or %2$s to mark. Games Previous Games Difficulty @@ -9,6 +20,7 @@ Beginner Intermediate Expert + With your help, we\'ll be able to implement new features and keep our project active. Open Settings Animations @@ -33,7 +45,6 @@ System Feedback Support us! - With your help, we\'ll be able to implement new features and keep our project active. To support If you like this game, please give us a feedback. It will help us a lot. This game uses the following third parties software: @@ -65,6 +76,9 @@ Flag Retry Empty + Impossible to do that now + You have received: +%1$d + For more Help, you must win a game. Unknown error. Leaderboards Cancel @@ -72,7 +86,7 @@ Yes Unlock Achievements - No + No General Source Code Translation @@ -103,4 +117,7 @@ Flag placed! Flag removed! Remove Ads + Help + You have to clear a rectangular board containing hidden mines without detonating any of them. + Antimine diff --git a/foss/build.gradle b/foss/build.gradle index c5bc3bc4..2082bfd3 100644 --- a/foss/build.gradle +++ b/foss/build.gradle @@ -6,8 +6,8 @@ android { compileSdkVersion 30 defaultConfig { - versionCode 800111 - versionName '8.0.11' + versionCode 800101 + versionName '8.1.0' minSdkVersion 21 targetSdkVersion 30 } diff --git a/proprietary/build.gradle b/proprietary/build.gradle index 79b46714..1d072297 100644 --- a/proprietary/build.gradle +++ b/proprietary/build.gradle @@ -10,8 +10,8 @@ android { compileSdkVersion 30 defaultConfig { - versionCode 800111 - versionName '8.0.11' + versionCode 800101 + versionName '8.1.0' minSdkVersion 21 targetSdkVersion 30 } diff --git a/wear/build.gradle b/wear/build.gradle index 40e213c0..9380a4f5 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -8,8 +8,8 @@ android { defaultConfig { // versionCode and versionName must be hardcoded to support F-droid - versionCode 800111 - versionName '8.0.11' + versionCode 800101 + versionName '8.1.0' applicationId 'dev.lucasnlm.antimine' minSdkVersion 23 targetSdkVersion 30 diff --git a/wear/src/main/java/dev/lucasnlm/antimine/wear/WatchLevelFragment.kt b/wear/src/main/java/dev/lucasnlm/antimine/wear/WatchLevelFragment.kt index 0b70b8e2..cc34b3ab 100644 --- a/wear/src/main/java/dev/lucasnlm/antimine/wear/WatchLevelFragment.kt +++ b/wear/src/main/java/dev/lucasnlm/antimine/wear/WatchLevelFragment.kt @@ -17,8 +17,6 @@ class WatchLevelFragment : CommonLevelFragment(R.layout.fragment_level) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - recyclerGrid = view.findViewById(R.id.recyclerGrid) recyclerGrid.doOnLayout { lifecycleScope.launch { val levelSetup = gameViewModel.loadLastGame() diff --git a/wear/src/main/res/values-da-rDK/strings.xml b/wear/src/main/res/values-da-rDK/strings.xml index dd74906f..2243832c 100644 --- a/wear/src/main/res/values-da-rDK/strings.xml +++ b/wear/src/main/res/values-da-rDK/strings.xml @@ -1,6 +1,6 @@ - New Game - You won! 😎 - Exploded! 😢 + Nyt spil + Du vandt! 😎 + Eksploderet! 😢 diff --git a/wear/src/main/res/values-iw-rIL/strings.xml b/wear/src/main/res/values-iw-rIL/strings.xml index dd74906f..370b7a6c 100644 --- a/wear/src/main/res/values-iw-rIL/strings.xml +++ b/wear/src/main/res/values-iw-rIL/strings.xml @@ -1,6 +1,6 @@ - New Game - You won! 😎 - Exploded! 😢 + משחק חדש + נצחון! 😎 + פיצוץ! 😢 diff --git a/wear/src/main/res/values-ko-rKR/strings.xml b/wear/src/main/res/values-ko-rKR/strings.xml index dd74906f..86084834 100644 --- a/wear/src/main/res/values-ko-rKR/strings.xml +++ b/wear/src/main/res/values-ko-rKR/strings.xml @@ -1,6 +1,6 @@ - New Game - You won! 😎 - Exploded! 😢 + 새 게임 + 승리! 😎 + 펑! 게임 오버! 😢