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 @@
Sí
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 @@
Sí
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 @@
Sì
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 @@
Có
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! 😢
+ 새 게임
+ 승리! 😎
+ 펑! 게임 오버! 😢